On this page:

6.3LiveBundle Keepers

 

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 nameAggregation algorithm
LatestByPathKeep the most recent Root Resource. Earlier Root Resources are removed from the bundle. E.g. For each patient, store the most recent encounter.
LatestByParamPathKeep 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)
LatestByParamPathByMonthSame as LatestByParamPath except it keeps the most recent value for each month. Useful for charting.
EarliestByPathSame as LatestByPath except it keeps the earliest resources.
EarliestByParamPathSame as LatestByParamPath except it keeps the earliest resources.
EarliestByParamPathByMonthSame as LatestByParamPathByMonth except it keeps the earliest resources.
ToggleByPathThis 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.
ToggleBySearchThis 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.
WatchlistPopulatorUse 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

6.3.1LiveBundle Keeper Examples

 

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_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_NO_REFERENCES', toggleByPathKeeperNoReferences()));
   ruleSet.addRule(encounterRule('TOGGLE_BY_SEARCH', toggleBySearchKeeper()));
   ruleSet.addRule(encounterRule('WATCHLIST_POPULATOR', watchlistPopulatorKeeper()));

   let latestByPathByTrackingIdRule = encounterRule('LATEST_BY_PATH_BY_TRACKING_ID', latestByPathByTrackingIdKeeper())
   latestByPathByTrackingIdRule.setTrackingType('Organization');
   ruleSet.addRule(latestByPathByTrackingIdRule);

   return ruleSet;
}

Here now are each of the Keepers with their definitions and examples.

6.3.2LatestByPath

 
LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate)
// or
LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate, numberToKeep)
ParameterTypeDescription
pathToOrderDateStringThe 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.
numberToKeepInteger(Optional: default 1) The number of resources to keep

LatestByPath Example

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);
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. Build two Encounters, oldEncounter and newEncounter and set the subject of each to this patient

  3. Set the period.start of oldEncounter to an earlier date and period.start of newEncounter to a later date

  4. create oldEncounter and newEncounter in either order.

  5. Request the LATEST_BY_PATH bundle for this patient. It contains only the newEncounter.

LatestByPath TrackingId Example

In this scenario, a patient visits multiple organizations and we want to track their latest visit by organization. We do this by setting the pathToTrackingId to the FHIR path on the Encounter that gives us the Organization id we want to track by.

function latestByPathByTrackingIdKeeper() {
   // This is the path we use to determine to find the date to pick the most recent one
   let pathToOrderDate = 'period.start';

   keeper = LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate);
   keeper.setPathToTrackingId('serviceProvider');
   return keeper;
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. create Organization organization1

  3. create Encounter oldEncounter1 with these values: subject = patient, serviceProvider = organization1, period.start = an earlier date

  4. Request the LATEST_BY_PATH_BY_TRACKING_ID bundle for organization1. It contains the oldEncounter1.

  5. create Encounter newEncounter1 with these values: subject = patient, serviceProvider = organization1, period.start = a later date

  6. Request the LATEST_BY_PATH_BY_TRACKING_ID bundle for organization1. It now contains the newEncounter1.

  7. create Organization organization2

  8. create Encounter oldEncounter2 with these values: subject = patient, serviceProvider = organization2, period.start = an earlier date

  9. Request the LATEST_BY_PATH_BY_TRACKING_ID bundle for organization1. It still contains the newEncounter1.

  10. Request the LATEST_BY_PATH_BY_TRACKING_ID bundle for organization2. It contains the oldEncounter2.

6.3.3LatestByParamPath

 
LiveBundleKeeperFactory.newLatestByParamPath(pathToLatestParam, pathToOrderDate);
// or
LiveBundleKeeperFactory.newLatestByParamPath(pathToLatestParam, pathToOrderDate, numberToKeep);
ParameterTypeDescription
pathToLatestParamStringA 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'.
pathToOrderDateStringThe 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.
numberToKeepInteger(Optional: default 1) The number of resources to keep

By default, the trackingIdPath for all Keepers is the Filter's pathToSubscriber. However, the Latest Keepers also allow for a different pathToTrackingId to be specified by calling keeper.setPathToTrackingId(pathToTrackingId). See LatestByPath TrackingId Example for an example of how to track latest values by a different path than the subscriber.

LatestByParamPath Example

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);
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. Build two Encounters, oldAmbulatoryEncounter and newEmergencyEncounter and set the subject of each to this patient

  3. Set the period.start of oldAmbulatoryEncounter to an earlier date and period.start of newEncounter to a later date

  4. Set the class of oldAmbulatoryEncounter to 'AMB' and the class of newEmergencyEncounter to 'EMER'.

  5. create oldAmbulatoryEncounter and newEmergencyEncounter in either order.

  6. Request the LATEST_BY_PARAM_PATH bundle for this patient. It contains both oldAmbulatoryEncounter and newEmergencyEncounter since they have different classes.

  7. create a third Encounter, newAmbulatoryEncounter, and set its period.start to the later date and its class to 'AMB'

  8. 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.

6.3.4LatestByParamPathByMonth

 

This is the same as LatestByParamPath except that it stores one latest value for each month.

LiveBundleKeeperFactory.newLatestByParamPathByMonth(pathToLatestParam, pathToOrderDate)
ParameterTypeDescription
pathToLatestParamStringA 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'.
pathToOrderDateStringThe 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.
numberToKeepInteger(Optional: default 1) The number of resources to keep

LatestByParamPathByMonth Example

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);
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. Build two Encounters, oldLastMonthAmbulatoryEncounter and newLastMonthAmbulatoryEncounter and set the subject of each to this patient

  3. 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

  4. Set the class of both to 'AMB'

  5. create oldLastMonthAmbulatoryEncounter and newLastMonthAmbulatoryEncounter in either order.

  6. 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.

  7. create a third Encounter, oldThisMonthAmbulatoryEncounter, and set its period.start to an early date this month and set its class to 'AMB'

  8. 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.

  9. create a fourth Encounter, newThisMonthEmergencyEncounter, set its period.start to a later date this month, and set its class to 'EMER'

  10. 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.

6.3.5ToggleByPath

 

Toggle Keepers switch LiveBundles for a Root Resource on and off based on whether the Root Resource passes the specified keepFilter.

	LiveBundleKeeperFactory.newToggleByPath(keepFilter, keepReferencesPath);
	// or
	LiveBundleKeeperFactory.newToggleByPath(keepFilter, keepReferencesPath, pathToOrderDate);
ParameterTypeDescription
keepFilterLiveBundleFilterReferences 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.
keepReferencesPathStringIn addition to the Root Resource, all references pointed to by this path are also added to or removed from the LiveBundle. If you only need to store the root resource and no references, set this parameter to an empty string. See function toggleByPathKeeperNoReferences() below for an example of how to do this.
pathToOrderDateString(Optional) The FHIR Path within the Root Resource for locating the date used to determine sort order of responses if a sort is requested.

ToggleByPath Example

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')
         .setPathToSubscriber('subject');

      let keepReferencesPath = 'episodeOfCare';

      // This is an optional parameter for toggleKeepers
      let pathToOrderDate = 'period.start';

      return LiveBundleKeeperFactory.newToggleByPath(keepFilter, keepReferencesPath, pathToOrderDate);
}

Scenario

  1. create two EpisodeOfCare resources

  2. create an Encounter with references to both of these EpisodeOfCare resources and set the status of this Encounter to 'in-progress'

  3. Request the TOGGLE_BY_PATH bundle for this patient. It contains the Encounter and both EpisodeOfCare resources.

  4. Set the status of the Encounter to 'finished' and update the Encounter.

  5. Request the TOGGLE_BY_PATH bundle for this patient. It is now empty.

6.3.6ToggleBySearch

 

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);
	// or
	LiveBundleKeeperFactory.newToggleBySharedReferenceSearch(keepFilter, pathToSharedReference, searchURL, pathToOrderDate);
ParameterTypeDescription
keepFilterLiveBundleFilterReferences 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.
pathToSharedReferenceStringThis is a path within the Root Resource to a reference that is used to search for related resources
searchURLStringThis 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
pathToOrderDateString(Optional) The FHIR Path within the Root Resource for locating the date used to determine sort order of responses if a sort is requested.

ToggleBySearch Example

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')
         .setPathToSubscriber('subject');

      let pathToSharedReference = 'episodeOfCare';

      let searchURL = 'MedicationDispense?status=completed&context='

      return LiveBundleKeeperFactory.newToggleBySharedReferenceSearch(keepFilter, pathToSharedReference, searchURL);
}

Scenario

  1. create two EpisodeOfCare resources

  2. For each of these EpisodeOfCare resources, create a 'completed' MedicationDispense with a 'context' pointing to the EpisodeOfCare.

  3. create an Encounter with references to both of these EpisodeOfCare resources and set the status of this Encounter to 'in-progress'

  4. Request the TOGGLE_BY_SEARCH bundle for this patient. It contains the Encounter, both EpisodeOfCare resources, and both MedicationDispenses.

  5. Set the status of the Encounter to 'finished' and update the Encounter.

  6. Request the TOGGLE_BY_SEARCH bundle for this patient. It is now empty.

6.3.7WatchlistPopulator

 

Most keepers store LiveBundle records. The WatchListPopulator keeper, however, adds subscribers to a second watchlist.

	return LiveBundleKeeperFactory.newWatchlistPopulator(keepFilter, system, name, pathToAddedSubscriber);
ParameterTypeDescription
keepFilterLiveBundleFilterReferences 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.
systemStringThe system of the watchlist found references should be subscribed to
nameStringThe name of the watchlist found references should be subscribed to
pathToAddedSubscriberStringA FHIR path within the Root Resource to find references to add to the specified watchlist

WatchlistPopulator Example

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')
         .setPathToSubscriber('subject');

      let watchlistToPopulate = 'APPOINTMENT_WATCHLIST';

      let pathToAddedSubscriber = 'appointment';

      return LiveBundleKeeperFactory.newWatchlistPopulator(keepFilter, EXAMPLE_SYSTEM, watchlistToPopulate, pathToAddedSubscriber);
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. Build an Encounters and set the subject of it to this patient

  3. create two new Appointments and add them to this Encounter

  4. Set the status of this Encounter to 'in-progress'

  5. create the Encounter

  6. The APPOINTMENT_WATCHLIST now contains both of these Appointments.

LatestByPath Example keeping latest 3 values

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, 3);
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. Build five Encounters, and set the subject of each to this patient

  3. Set the period.start of the Encounters to different dates

  4. create the five Encounters in any order.

  5. Request the LATEST_THREE_BY_PATH bundle for this patient. It contains only the Encounters with the three most recent dates.

EarliestByPath Example keeping earliest 3 values

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, 3);
}

Scenario

  1. create a new patient and subscribe it to PATIENT_WATCHLIST

  2. Build five Encounters, and set the subject of each to this patient

  3. Set the period.start of the Encounters to different dates

  4. create the five Encounters in any order.

  5. Request the EARLIEST_THREE_BY_PATH bundle for this patient. It contains only the Encounters with the three earliest dates.

LatestByPath Example with no references

function toggleByPathKeeperNoReferences() {
      let keepFilter = LiveBundleFilter.create()
         .setRootResourceType('Encounter')
         .setCriteria('status=in-progress')
         .setPathToSubscriber('subject');

      // Only save root resources
      let keepReferencesPath = '';

      // This is an optional parameter for toggleKeepers
      let pathToOrderDate = 'period.start';

      return LiveBundleKeeperFactory.newToggleByPath(keepFilter, keepReferencesPath, pathToOrderDate);
}