Placeholder Touching

From DOC

Jump to: navigation, search

Note: This article describes an experimental feature added in org.tolven.component.tolvenejb version 0.0.64.

Tolven makes every effort to optimize end-user response time. A number of features contribute to this ability. One is that rules are used to maintain specific lists in the database that correspond to lists defined in the user interface. In a general sense, this is sometimes referred to as denormalization. However, many lists are the result of complex and sometimes pragmatic (human) decisions and therefore do not always lend themselves to pure SQL queries.

As a result, Tolven uses a combination of optimized SQL and rules to populate lists used in user interfaces. As discussed elsewhere, virtually all data in Tolven originates as a document, delivered to Tolven via message. Even data originating within Tolven applications follows this same route. That is, a data entry application constructs a document of some sort interactively. Upon submit, the document is queued for later processing (usually withing milliseconds).

During message processing, rules extract data from the document and create Placeholders. Think of placeholder as a synonym for entity. In the following example, we will use patient and encounter as examples of placeholders. The actual placeholders in a Tolven system are defined in metadata and typically include observations, problems, allergies, etc.

Note: By patient, we usually mean "patient demographics", not the entire patient record. And, by encounter, we usually mean "patient encounter with a healthcare worker". It is our custom to store the encounter record with the patient and then cross-reference the encounter with each of the healthcare workers, appointment books, locations, etc, associated with the encounter.

Some placeholders refer to other placeholders. In the following example, an encounter will refer to a patient. The converse relationship will also exist and is the focus of this article. In the following scenario, assume a patient exists in the system and that at least one encounter already exists for that patient. An encounter occurs between a health-care provider and a patient. For example, if you, the patient, goes to the doctor (a provider) for a physical, that is one encounter.

Contents

Example application

This is an example of an inpatient encounter list maintained for a physician (echr:assigned:encounters:in). Notice in this example that the Patient column references the patient placeholder. These references to the patient are evaluated at the time the encounter is stored in the physician's encounter list, not when the patient placeholder is updated.

<extends path="echr:assigned">
    <menu name="encounters" ...>
        <list name="in" ...>
            <column name="Patient" internal="string01" ...>
                <from>#{encounter.patient.lastName}, #{encounter.patient.firstName} - #{encounter.patient.sex} </from>
            </column>
        </list>
    </menu>
</extends>

If the patient were later to be updated, the update would not make it to this list. While that may be desirable behavior in some cases, it is not in this case.

Possible approaches:

Approach Comment
Normalize the database so that the patient is accessed each time the list is displayed This is not always the desired behavior since it may not carry sufficient audit trail behavior. Nor is it efficient, especially in the case whrer patient data is not maintained in the same database as encounter data.
When the patient is updated, update all lists that reference that patient This has the desired effect of performing the update work in the background, not at query time. However, it misses an important detail: The rules may have used patient information to cause the encounter to be on that list. If that data changed, then existence on that list may no longer be valid. Removing a patient from a list may not be simple - it may require notification or other side effects.
Rerun the rules for the encounter placeholder when the patient is updated This allows rules to have complete control of the update. Not only can the encounter and all references to it be updated with the new patient data, the rules can be selective about which data is updated, what audit trail is created, if the item should be removed from one list and possibly added to another list, etc. And, of course, it operates in the background so that end-user performance is not impacted.

the last approach is the one used in Tolven.

A more Complex Scenario

Say that a new encounter placeholder being processed by rules refers to a patient placeholder that already exists and refers to a provider placeholder that already exists. Rules are likely to put the encounter on at least two lists: a list of encounters for the patient (listing the provider and other details about the encounter) and a list of encounters for the provider (listing the name of the patient and other details about the encounter).

In essence, four database rows will exist for this encounter: The immutable document containing the encounter, an encounter placeholder, and the two list entries that were created. This may seem overly complicated for such a simple situation, but the reality is more complex. We'll stay with the simple example nevertheless.

Each list entry contains information extracted from the encounter placeholder as well as items referenced by the encounter placeholder. For example, the provider's list of encounters would, at a minimum, contain the date of the encounter as well as the last name and first name of the patient. Something like this:

encounter.effectiveDateLow
encounter.patient.lastName
encounter.patient.firstName

When the encounter placeholder is updated, it will grab fresh versions of this data. For example, if the patient name had changed in the meantime, it will use the new patient name on the list.

This approach improves performance by limiting the need for many database joins: The list on the screen is usually very close to the form of the list in the database.

However, when the patient placeholder changes, the denormalized data in the encounter list becomes out of date (stale). In order to freshen the stale data in the encounter list, each encounter placeholder referencing that patient must be re-run in the rules. This will recalculate the data in the encounter list.

The process of re-running rules in this way does not change any placeholder data or document data, only the data derived from placeholders (lists, etc).

How it works

In normal rule processing, each new or modified placeholder is asserted into working memory so that rules can determine which placeholders to add to which lists (or any other behavior desired). However, when the patient placeholder data changes (but not the encounter) the encounter list data that had previously been extracted from the patient placeholder becomes stale. The solution is to reevaluate the encounter placeholders that refer to that patient. This process is called "touching" in Tolven (after the touch utility in UNIX). In this case, the placeholders that need to be touched are all encounter placeholders that refer to that patient.

Touching an encounter placeholder will cause the updated patient placeholder data to be included in encounter list items. But the approach goes further: List membership may also change due to a change in a referenced placeholder. For example, if a rule put a particular encounter on a particular list because the patient was, say, a certain gender, and the patient gender information changed, then the existence of the encounter on that list may also change.

As you may have noticed, touching can possibly be a long operation. For example, say the provider's phone number changes. This is an important piece of information that should be reflected in each encounter related to that provider.

Tolven breaks up placeholder processing in two phases. The first phase handles the direct updates, in the case we're taking about, a change made to the patient placeholder. In this phase, "touch" messages are created for any placeholders referencing the just-changed patient placeholder. This allows the initial message to finish and at least get the primary data (the provider's phone number) available for direct viewing as well as reference by new encounters. The queued touch messages will then be processed, one at a time. Each will cause the affected placeholder to be reevaluated by rules.

Identifying placeholders that need touching

In general, all placeholders that depend on the placeholder being updated should be considered touched. You can actually narrow the selection a bit and say each active encounter or future encounter (appointment) should have an updated phone number while historical (past) encounters can be left alone. That still could mean thousands or maybe tens of thousands of encounters need updating.

In any case, an explicit rule action requests that touching be enabled for that placeholder (not a type of placeholder but rather a specific instance of a placeholder). Therefore, if the encounter placeholder does not request to be touched if a referenced placeholder is changed, then it won't be touched.

Special considerations

As already described, placeholders are not normally modified by the touch process. Nevertheless, rules that react to placeholders may cause actions that are difficult to reverse. For example, say that a certain physician is notified by pager whenever a new patient encounter is added to their encounter list. The encounter may have been placed on that list in part due to specific settings in patient demographics. If the patient's demographics changes, it would not be appropriate to simply remove the patient from the provider's encounter list. In such a case, the rules may need to send another page or email to the provider indicating the change.

Computed Fields

Tolven has the ability to directly access fields in other placeholders from a list. Removing computes that reference other placeholders will improve performance because it invariably eliminates joins. Further, computed fields do not provide sorting or filtering as normal internal fields of a list do.

Rules

The simplest and most efficient way to manage touching is to write a rule that enables touching for a given placeholder. In this method, you must know ahead of time that a change to either of the related placeholders (patient and/or provider) is important to the encounter placeholder.

rule
   when
      $encounter: encounter( actStatus=="active", $patient: patient, $provider: provider)
   then
      app.touchIf( $encounter, $patient );
      app.touchIf( $encounter, $provider );
end

This approach populates a touch table that causes the encounter to be touched if either the patient or the provider changes. If the encounter is not active, it will be removed from the touch table. (Actually, it is marked as deleted). Your rule may vary. For example, if you don't care about changes to the provider, you can remove app.touchIf( ..., $provider).

Everything else is handled automatically: The encounter will be reevaluated each time the patient or the provider referenced by this encounter placeholder changes. A Touch will be removed unless renewed by the touch rule, such as in the example above. This may happen during normal placeholder rule processing or during a touch because the same rule that initiated the touch will run again after the touch.

Touch Mode

Placeholders asserted into working memory are contained within a Placeholder Fact wrapper. When this placeholder fact is in working memory because the underlying placeholder has been touched, a special flag is set in the wrapper. (The flag is not set on the placeholder itself.) This flag can be used in rules that may need to distinguish between regular placeholder processing and touch processing.

You might want to avoid updating a certain list simply because the placeholder is touched. For example, as tempting as it might be, a list used for audit purposes might be inappropriate to modify on a touch. This might occur on a an encounter audit log. While a phone number might have changed in the meantime, the log should probably contain the phone numbers available at the time, not what they might be later.

rule
   when
      $encounter: encounter( touching==false )
   then
      app.createReferenceMD( $encounter, "echr:auditLog" );
end

Scheduled Touch

This section describes a future capability - and is thus still TDB

Sometimes, circumstances may change simply due to the passage of time. For example, if a patient is on (or not on) a certain list due to their age, membership on that list may change at a point in the future due to the patient's age. In this case, a scheduled touch would be appropriate. Essentially, a schedule touch is saying, review this placeholder at a time in the future.

A touch can refer to itself but only when it is scheduled for a future date.

rule
   when
      $encounter: encounter( actStatus=="active", $patient: patient, $provider: provider)
   then
      app.touchWhen( $encounter, $encounter.getField("effectiveTime").add(30*days) );
end

A scheduled touch can be used for the automatic cleanup of old data. (Set the encounter as inactive if there has been no activity for a certain period). This will also cause the touchIf's above to be disabled since the encounter would then be inactive. The encounter is effectively quiesced: It no longer participates in touch processing.

SQL Query

The following might be useful for debugging:

select t.*, mu.menu_path as "Update path", mu.string01 as "update", mf.menu_path as "Focal path", mf.string01 as "focal" 
from touch t, app.menu_data mu, app.menu_data mf 
where t.updateplaceholder_id = mu.id 
and t.focalplaceholder_id = mf.id and (t.deleted is null or t.deleted = false) 
order by t.account_id, t.updateplaceholder_id;
Personal tools