A Keeper defines the aggregation rules used to decide which references to keep when a Root Resource matches a Filter. Here is a summary of the different kinds of keepers currently available.
Keeper name | Aggregation algorithm |
---|---|
LatestByPath | Keep the most recent Root Resource. Earlier Root Resources are removed from the bundle. E.g. For each patient, store the most recent encounter. |
LatestByParamPath | Keep one copy of a Root Resource for each different value of the provided parameter. E.g. For each patient, store the most recent Observation for each different type of Observation (weight, height, BP etc) |
LatestByParamPathByMonth | Same as LatestByParamPath except it keeps the most recent value for each month. Useful for charting. |
EarliestByPath | Same as LatestByPath except it keeps the earliest resources. |
EarliestByParamPath | Same as LatestByParamPath except it keeps the earliest resources. |
EarliestByParamPathByMonth | Same as LatestByParamPathByMonth except it keeps the earliest resources. |
ToggleByPath | This Keeper keeps all references indicated by a path provided. This keeper includes a KeeperFilter that is used to toggle whether the path references should be added to or removed from the LiveBundle. E.g. To maintain a list of patients on a ward, you could create a ToggleFilter on Encounters that toggle based on whether the Encounter has finished. |
ToggleBySearch | This is the same as ToggleByPath except instead instead of pulling references from a path on the Root Resource, the FHIR Storage repository is searched for references to store based on a provided search URL appended with a specified reference id. |
WatchlistPopulator | Use this keeper to populate one watchlist from another, e.g. when a Practioner is subscribed to a PRACTITIONER_WATCHLIST, this rule could automatically subscribe all of their Patients to a PATIENT_WATCHLIST |
Keeper algorithms are best explained using examples. Each Keeper is defined below followed by an example configuration for that keeper and an example scenario that explains which references are saved for various incoming resources.
In all of the scenarios below, we are watching Encounters as they arrive and saving bundles for them. In all examples, we only process Encounters that take place at the PED.DIABETES
location
for patients subscribed to the PATIENT_WATCHLIST
.
All of the examples below share these Rule and Filter definitions:
const EXAMPLE_SYSTEM = 'myorg.org/diabetes_management';
function buildLiveBundleRuleSet() {
let ruleSet = LiveBundleRuleSet.create();
// Add Watchlists
ruleSet.addWatchlist(LiveBundleWatchlist.create(EXAMPLE_SYSTEM, 'PATIENT_WATCHLIST', 'Patient'));
ruleSet.addWatchlist(LiveBundleWatchlist.create(EXAMPLE_SYSTEM, 'APPOINTMENT_WATCHLIST', 'Appointment'));
ruleSet.addWatchlist(LiveBundleWatchlist.create(EXAMPLE_SYSTEM, 'OTHER_WATCHLIST', 'Appointment'));
// Add Rules
ruleSet.addRule(encounterRule('LATEST_BY_PATH', latestByPathKeeper()));
ruleSet.addRule(encounterRule('LATEST_BY_PATH_KEEP_REFERENCES', latestByPathKeepReferencesKeeper()));
ruleSet.addRule(encounterRule('LATEST_BY_PATH_WITH_KEEPFILTER', latestByPathWithKeepFilterKeeper()));
ruleSet.addRule(encounterRule('LATEST_THREE_BY_PATH', latestThreeByPathKeeper()));
ruleSet.addRule(encounterRule('EARLIEST_THREE_BY_PATH', earliestThreeByPathKeeper()));
ruleSet.addRule(encounterRule('LATEST_BY_PARAM_PATH', latestByParamPathKeeper()));
ruleSet.addRule(encounterRule('LATEST_BY_PARAM_PATH_BY_MONTH', latestByParamPathByMonthKeeper()));
ruleSet.addRule(encounterRule('TOGGLE_BY_PATH', toggleByPathKeeper()));
ruleSet.addRule(encounterRule('TOGGLE_BY_PATH_MULTI_REFERENCES', toggleByPathKeeperMultipleKeepReferences()));
ruleSet.addRule(encounterRule('TOGGLE_BY_PATH_NO_REFERENCES', toggleByPathKeeperNoReferences()));
ruleSet.addRule(encounterRule('TOGGLE_BY_SEARCH', toggleBySearchKeeper()));
ruleSet.addRule(encounterRule('WATCHLIST_POPULATOR', watchlistPopulatorKeeper()));
return ruleSet;
}
Here now are each of the Keepers with their definitions and examples.
LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate)
Parameter | Type | Description |
---|---|---|
pathToOrderDate | String | The FHIR Path within the Root Resource for locating the date used to determine the most recent resource. This date is also used to determine sort order of responses if a sort is requested. |
In this scenario, we store the most recent Encounter for all patients on the watchlist.
function latestByPathKeeper() {
// This is the path we use to determine to find the date to pick the most recent one
let pathToOrderDate = 'period.start';
return LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate);
}
create a new patient and subscribe it to PATIENT_WATCHLIST
Build two Encounters, oldEncounter and newEncounter and set the subject
of each to this patient
Set the period.start
of oldEncounter to an earlier date and period.start
of newEncounter to a later date
create oldEncounter and newEncounter in either order.
Request the LATEST_BY_PATH
bundle for this patient. It contains only the newEncounter.
LiveBundleKeeperFactory.newLatestByParamPath(pathToLatestParam, pathToOrderDate);
Parameter | Type | Description |
---|---|---|
pathToLatestParam | String | A latest resource reference is stored for each different value of this param. E.g. to store the latest Observation by code for a patient, you would use 'code.coding.code'. |
pathToOrderDate | String | The FHIR Path within the Root Resource for locating the date used to determine the most recent resource. This date is also used to determine sort order of responses if a sort is requested. |
In this scenario, we don't just want to store the most recent Encounter for every patient, but we actually want to store the most recent Encounter of each class
for every patient. So for example, if a patient has both ambulatory and emergency encounters, we will store the most recent encounter for each of those.
function latestByParamPathKeeper() {
// This is the path we use to find the parameter we store LiveBundles by.
let pathToParam = 'class';
// This is the path we use to determine to find the date to pick the most recent one
let pathToOrderDate = 'period.start';
return LiveBundleKeeperFactory.newLatestByParamPath(pathToParam, pathToOrderDate);
}
create a new patient and subscribe it to PATIENT_WATCHLIST
Build two Encounters, oldAmbulatoryEncounter and newEmergencyEncounter and set the subject
of each to this patient
Set the period.start
of oldAmbulatoryEncounter to an earlier date and period.start
of newEncounter to a later date
Set the class of oldAmbulatoryEncounter to 'AMB' and the class of newEmergencyEncounter to 'EMER'.
create oldAmbulatoryEncounter and newEmergencyEncounter in either order.
Request the LATEST_BY_PARAM_PATH
bundle for this patient. It contains both oldAmbulatoryEncounter and newEmergencyEncounter since they have different classes.
create a third Encounter, newAmbulatoryEncounter, and set its period.start
to the later date and its class to 'AMB'
Request the LATEST_BY_PARAM_PATH
bundle for this patient. It now contains newAmbulatoryEncounter and newEmergencyEncounter since newAmbulatoryEncounter replaces oldAmbulatoryEncounter since they have the same class.
This is the same as LatestByParamPath
except that it stores one latest value for each month.
LiveBundleKeeperFactory.newLatestByParamPathByMonth(pathToLatestParam, pathToOrderDate)
Parameter | Type | Description |
---|---|---|
pathToLatestParam | String | A latest resource reference is stored for each different value of this param. E.g. to store the latest Observation by code for a patient, you would use 'code.coding.code'. |
pathToOrderDate | String | The FHIR Path within the Root Resource for locating the date used to determine the most recent resource. This date is also used to determine sort order of responses if a sort is requested. |
function latestByParamPathByMonthKeeper() {
// This is the path we use to find the parameter we store LiveBundles by.
let pathToParam = 'class';
// This is the path we use to determine to find the date to pick the most recent one
let pathToOrderDate = 'period.start';
return LiveBundleKeeperFactory.newLatestByParamPathByMonth(pathToParam, pathToOrderDate);
}
create a new patient and subscribe it to PATIENT_WATCHLIST
Build two Encounters, oldLastMonthAmbulatoryEncounter and newLastMonthAmbulatoryEncounter and set the subject
of each to this patient
Set the period.start
of oldLastMonthAmbulatoryEncounter to an earlier day of last month and period.start
of newLastMonthAmbulatoryEncounter to a later day of last month
Set the class of both to 'AMB'
create oldLastMonthAmbulatoryEncounter and newLastMonthAmbulatoryEncounter in either order.
Request the LATEST_BY_PARAM_PATH_BY_MONTH
bundle for this patient. It contains only newLastMonthAmbulatoryEncounter since both Encounters have the same param value and same month.
create a third Encounter, oldThisMonthAmbulatoryEncounter, and set its period.start
to an early date this month and set its class to 'AMB'
Request the LATEST_BY_PARAM_PATH_BY_MONTH
bundle for this patient. It now contains two resources: newLastMonthAmbulatoryEncounter and oldThisMonthAmbulatoryEncounter since it saves a different latest Encounter for each month.
create a fourth Encounter, newThisMonthEmergencyEncounter, set its period.start
to a later date this month, and set its class to 'EMER'
Request the LATEST_BY_PARAM_PATH_BY_MONTH
bundle for this patient. It now contains three resources since newThisMonthEmergencyEncounter is for a different value of param.
Toggle Keepers switch LiveBundles for a Root Resource on and off based on whether the Root Resource passes the specified keepFilter.
LiveBundleKeeperFactory.newToggleByPath(keepFilter);
Parameter | Type | Description |
---|---|---|
keepFilter | LiveBundleFilter | References are added and removed to this LiveBundle according to whether the Root Resource passes this filter. The Root Resource Type of this filter must match the Root Resource Type of the Rule Filter. |
In this scenario, we want to maintain an active list of all in-progress
Encounters along with their EpisodeOfCare resources for subscribed patients. When the Encounter is updated and its status is changed, for example to finished
then this keeper removes the encounter and all of its associated EposideOfCare resources.
function toggleByPathKeeper() {
let keepFilter = LiveBundleFilter.create()
.setRootResourceType('Encounter')
.setCriteria('status=in-progress')
.setSubscriberSearchParam('subject');
return LiveBundleKeeperFactory.newToggleByPath(keepFilter)
.setKeepReferencesPath('episodeOfCare')
.setPathToOrderDate('period.start');
}
create two EpisodeOfCare resources
create an Encounter with references to both of these EpisodeOfCare resources and set the status of this Encounter to 'in-progress'
Request the TOGGLE_BY_PATH
bundle for this patient. It contains the Encounter and both EpisodeOfCare resources.
Set the status of the Encounter to 'finished' and update the Encounter.
Request the TOGGLE_BY_PATH
bundle for this patient. It is now empty.
This is the same as above, except we have a list of FHIR paths of references we want to keep.
function toggleByPathKeeperMultipleKeepReferences() {
let keepFilter = LiveBundleFilter.create()
.setRootResourceType('Encounter')
.setCriteria('status=in-progress')
.setSubscriberSearchParam('subject');
return LiveBundleKeeperFactory.newToggleByPath(keepFilter)
.setKeepReferencesPath('episodeOfCare', 'diagnosis.condition', 'basedOn')
.setPathToOrderDate('period.start');
}
create two EpisodeOfCare resources
create one Condition resource
create an Encounter with references to both of the EpisodeOfCare resources and a diagnosis with the Condition resource. Set the status of this Encounter to 'in-progress'
Request the TOGGLE_BY_PATH
bundle for this patient. It contains the Encounter, both EpisodeOfCare resources, and the Condition resource.
Set the status of the Encounter to 'finished' and update the Encounter.
Request the TOGGLE_BY_PATH
bundle for this patient. It is now empty.
You use a ToggleBySearch keeper when all the references you need aren't directly available in the Root Resource, but you need to make a separate query to the FHIR Repository to find the references you need. This keeper is similar to the ToggleByPath keeper, except it stores references from this search of the FHIR Repository in addition to references in the Root Resource.
LiveBundleKeeperFactory.newToggleBySharedReferenceSearch(keepFilter, pathToSharedReference, searchURL);
Parameter | Type | Description |
---|---|---|
keepFilter | LiveBundleFilter | References are added and removed to this LiveBundle according to whether the Root Resource passes this filter. The Root Resource Type of this filter must match the Root Resource Type of the Rule Filter. |
pathToSharedReference | String | This is a path within the Root Resource to a reference that is used to search for related resources |
searchURL | String | This is a search url that ends with "=". The value of the shared reference is appended to this searchURL when searching for related references to update the LiveBundle |
In this example, we want to maintain a list of all 'completed' MedicationDispenses associated with our Encounters. The 'pathToSharedReference' in this example points to EpisodeOfCare references. It is "shared" in the sense that both the Encounter and MedicationDispenses share a reference to the same EpisodeOfCare resources. The Encounter Root Resource, MedicationDispense and linking EpisodeOfCare resources are all saved in the bundle.
function toggleBySearchKeeper() {
let keepFilter = LiveBundleFilter.create()
.setRootResourceType('Encounter')
.setCriteria('status=in-progress')
.setSubscriberSearchParam('subject');
let pathToSharedReference = 'episodeOfCare';
let searchURL = 'MedicationDispense?status=completed&context='
return LiveBundleKeeperFactory.newToggleBySharedReferenceSearch(keepFilter, pathToSharedReference, searchURL);
}
create two EpisodeOfCare resources
For each of these EpisodeOfCare resources, create a 'completed' MedicationDispense with a 'context' pointing to the EpisodeOfCare.
create an Encounter with references to both of these EpisodeOfCare resources and set the status of this Encounter to 'in-progress'
Request the TOGGLE_BY_SEARCH
bundle for this patient. It contains the Encounter, both EpisodeOfCare resources, and both MedicationDispenses.
Set the status of the Encounter to 'finished' and update the Encounter.
Request the TOGGLE_BY_SEARCH
bundle for this patient. It is now empty.
Most keepers store LiveBundle records. The WatchListPopulator
keeper, however, adds subscribers to a second watchlist.
LiveBundleKeeperFactory.newWatchlistPopulator(keepFilter, system, name, pathToAddedSubscriber);
Parameter | Type | Description |
---|---|---|
keepFilter | LiveBundleFilter | References are extracted from Root Resources that match this filter and added to the specified watchlist. The Root Resource Type of this filter must match the Root Resource Type of the Rule Filter. |
system | String | The system of the watchlist found references should be subscribed to |
name | String | The name of the watchlist found references should be subscribed to |
pathToAddedSubscriber | String | A FHIR path within the Root Resource to find references to add to the specified watchlist |
In this example, we watch for in-progress
Encounters and then store all of their Appointments to the APPOINTMENT_WATCHLIST.
function watchlistPopulatorKeeper() {
let keepFilter = LiveBundleFilter.create()
.setRootResourceType('Encounter')
.setCriteria('status=in-progress')
.setSubscriberSearchParam('subject');
let watchlistToPopulate = 'APPOINTMENT_WATCHLIST';
let pathToAddedSubscriber = 'appointment';
return LiveBundleKeeperFactory.newWatchlistPopulator(keepFilter, EXAMPLE_SYSTEM, watchlistToPopulate, pathToAddedSubscriber);
}
create a new patient and subscribe it to PATIENT_WATCHLIST
Build an Encounters and set the subject
of it to this patient
create two new Appointments and add them to this Encounter
Set the status of this Encounter to 'in-progress'
create the Encounter
The APPOINTMENT_WATCHLIST
now contains both of these Appointments.
In this scenario, we store the three most recent Encounters for a patient on the watchlist.
function latestThreeByPathKeeper() {
// This is the path we use to determine to find the date to pick the most recent one
let pathToOrderDate = 'period.start';
return LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate)
.setNumberToKeep(3);
}
create a new patient and subscribe it to PATIENT_WATCHLIST
Build five Encounters, and set the subject
of each to this patient
Set the period.start
of the Encounters to different dates
create the five Encounters in any order.
Request the LATEST_THREE_BY_PATH
bundle for this patient. It contains only the Encounters with the three most recent dates.
In this scenario, we store the three most recent Encounters for a patient on the watchlist.
function earliestThreeByPathKeeper() {
// This is the path we use to determine to find the date to pick the most recent one
let pathToOrderDate = 'period.start';
return LiveBundleKeeperFactory.newEarliestByPath(pathToOrderDate)
.setNumberToKeep(3);
}
create a new patient and subscribe it to PATIENT_WATCHLIST
Build five Encounters, and set the subject
of each to this patient
Set the period.start
of the Encounters to different dates
create the five Encounters in any order.
Request the EARLIEST_THREE_BY_PATH
bundle for this patient. It contains only the Encounters with the three earliest dates.
The following optional setters are also available on Keepers. See the examples above for how these are used.
Setter | Available on | Parameter |
---|---|---|
setPathToOrderDate(String) | All keepers | The FHIR Path within the Root Resource for locating the date used to determine sort order of responses if a sort is requested. Note that for Latest and Earliest keepers this is mandatory and a part of the constructor for the keeper since it's required by those keepers to decide which resource referencess to keep. |
setKeepReferencesPath(String... paths) | All keepers | In addition to the Root Resource, all references pointed to by the provided FHIR paths are also added to or removed from the LiveBundle. This parameter can be one or more FHIR paths. |
setNumberToKeep(Number) | Latest/Earliest keepers | The number of latest/earliest resources to keep. Defaults to 1 if not provided |
setKeepFilter(LiveBundleFilter filter) | All keepers | Only root resources that pass this filter are stored |