20.3.1LiveBundle Keepers
Trial

 

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

20.3.2LiveBundle Keeper Examples
Trial

 

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.

20.3.3LatestByPath
Trial

 
    LiveBundleKeeperFactory.newLatestByPath(pathToOrderDate)
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.

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

20.3.3.2Scenario

  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.

20.3.4LatestByParamPath
Trial

 
    LiveBundleKeeperFactory.newLatestByParamPath(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.

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

20.3.4.2Scenario

  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.

20.3.5LatestByParamPathByMonth
Trial

 

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.

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

20.3.5.2Scenario

  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.

20.3.6ToggleByPath
Trial

 

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

    LiveBundleKeeperFactory.newToggleByPath(keepFilter);
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.

20.3.6.1ToggleByPath 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')
         .setSubscriberSearchParam('subject');

      return LiveBundleKeeperFactory.newToggleByPath(keepFilter)
         .setKeepReferencesPath('episodeOfCare')
         .setPathToOrderDate('period.start');
}

20.3.6.2Scenario

  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.

20.3.6.3ToggleByPath Multiple Keep References Example

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

20.3.6.4Scenario

  1. create two EpisodeOfCare resources

  2. create one Condition resource

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

  4. Request the TOGGLE_BY_PATH bundle for this patient. It contains the Encounter, both EpisodeOfCare resources, and the Condition resource.

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

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

20.3.7ToggleBySearch
Trial

 

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

20.3.7.1ToggleBySearch 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')
         .setSubscriberSearchParam('subject');

      let pathToSharedReference = 'episodeOfCare';

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

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

20.3.7.2Scenario

  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.

20.3.8WatchlistPopulator
Trial

 

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

	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

20.3.8.1WatchlistPopulator 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')
         .setSubscriberSearchParam('subject');

      let watchlistToPopulate = 'APPOINTMENT_WATCHLIST';

      let pathToAddedSubscriber = 'appointment';

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

20.3.8.2Scenario

  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.

20.3.8.3LatestByPath 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)
      .setNumberToKeep(3);
}

20.3.8.4Scenario

  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.

20.3.8.5EarliestByPath 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)
      .setNumberToKeep(3);
}

20.3.8.6Scenario

  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.

20.3.9Optional Keeper Parameters
Trial

 

The following optional setters are also available on Keepers. See the examples above for how these are used.

SetterAvailable onParameter
setPathToOrderDate(String)All keepersThe 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 keepersIn 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 keepersThe number of latest/earliest resources to keep. Defaults to 1 if not provided
setKeepFilter(LiveBundleFilter filter)All keepersOnly root resources that pass this filter are stored