Skip to main content

Adding a For Each Action

Adding a For Each Action

The for each action enables you to define a sequence actions that is executed iteratively, once for each member of one of the following:

  • A collection property (for a standard message) .

  • A repeating property (for a virtual document).

  • A set of subdocuments in a document (for a virtual document).

InterSystems IRIS represents each for each action as a connector line in the DTL diagram.

You can break out of a for each loop at any time by adding a break action within the loop.

To add a for each action:

  1. Select a collection or repeating property in the source message.

  2. Select for each from the Add Action drop-down list.

    On the Action tab, the Property field automatically contains the name of the selected source property, and the Key field automatically contains k1, as illustrated below:

    The foreach logic without the iterator key in the parentheses and with the key field completed

    For the for each action, the Key field specifies the name of an iterator variable.

    The Property field should not include the iterator key within the parentheses. For example, the following is correct:

    source.{PID:PatientIdentifierList( )}
    

    The for each iterates through the PatientIdentifierList repeating fields, starting with the first one (numbered 1) and ending with the last one.

  3. On the Action tab, the Unload check box controls whether to generate code to unload open objects or segments.

    If the unload is checked for a for each action, then code is generated in the Transform method to try to unload/unswizzle open object(s) or segment(s) for the property collection at the end of each loop. Unsaved virtual document segments are saved and finalized. If the property is the source object, the source object is usually already saved.

    You may still need to manually add actions to unload the target collection’s objects or segments. For details on some strategies, see Unloading Target Collections.

    The unload of the for each property collection may be unnecessary – for example, for HL7, code generated using CopyValues does not instantiate the source segments.

  4. To add actions to the for each block, click the for each action and then add the appropriate actions.

The details are then shown in the block below the DTL diagram. For example (partial):

DTL logic without the iterator key in the Property field and with a value in the key field.

If the <foreach> applies to a collection property in a message, the sequence of activities is executed iteratively, once for every element that exists within the collection property. If the element is null, the sequence is not executed. The sequence is executed if the element has an empty value, that is, the separators are there but there is no value between them, but is not executed for a null value, that is, the message is terminated before the field is specified.

Shortcuts for the For Each Action

When you are working with virtual documents, InterSystems IRIS provides a shortcut notation that iterates through every instance of a repeating field within a document structure. This means you do not actually need to set up multiple nested for each loops to handle repeating fields; instead you create a single assign action using a virtual property path with empty parentheses within the curly bracket { } syntax. For information, see Curly Bracket {} Syntax.

Note:

If the source and target types are different, you cannot use this shortcut for the for each action. Use an explicit for each action in these cases.

Unloading Target Collections

While the Unload option automatically removes objects from a source collection, you need to add custom code at the end of a for each action to remove objects from a target collection. In a simple example in which the target is a complex record, you could use the following code to save the current target record and then unload it:

Do target.Record16.GetAt(k1).%Save(0)
Do target.Record16.%UnSwizzleAt(k1)

In other scenarios, it might be better to avoid loading the target altogether in order to avoid issues where the target is not unloaded. For example, suppose you have an object that has a parent/child property with many children. Within the for each action, you have a subtransform combined with propSetObjectId(parentId)), where prop is the name of the property.

In this example, the target is the batch object, the target class is Demo.RecordMapBatch.Map.TrainDataOut.BatchOut and the record class is Demo.RecordMapBatch.Transform.Optimized.Record

Before your for each loop, you need to create an empty target and assign its ID to a property BatchOutID:

<assign value='target.%Save()' property='tSC' action='set' />
<assign value='target.%Id()' property='BatchOutID' action='set' />
<assign value='target' property='' action='set' />

Then, in the for each loop, you can use code that directly impacts the target without having the target instantiated. For example:

<assign value='""' property='record' action='set' />
<subtransform class='Demo.RecordMapBatch.Transform.Optimized.Record' targetObj='record' sourceObj='source.Records.(k1)' />

<comment>
<annotation>Assign record to target directly. </annotation>
</comment>
<assign value='record.%ParentBatchSetObjectId(BatchOutID)' property='tSC' action='set' />
<assign value='record.%Save()' property='tSC' action='set' />

Then, before the DTL ends, set the variable target back to the expected product of the DTL. For example:

<assign value='##class(Demo.RecordMapBatch.Map.TrainDataOut.BatchOut).%OpenId(BatchOutID)' property='target' action='set' />

Avoiding <STORE> Errors with Large Messages

As you loop over segments in messages or object collections, they are brought into memory. If these objects consume all the memory assigned to the current process, you may get unexpected errors. You can avoid these errors in the source collection by using the Unload option in the Management Portal. For some strategies for removing objects in a target collection, see Unloading Target Collections.

As another strategy, if you are processing many segments in a for each loop, you can call the commitSegmentByPath() method on both the source and target as the last step in the loop. Similarly, for object collections, use the %UnSwizzleAt() method.

The method commitCollectionOpenSegments() loops through the runtimePath looking for open segments within the specified collection path and calls commitSegmentByPath() for each open segment. This method is available from the classes EnsLib.EDI.X12.DocumentOpens in a new tab, EnsLib.EDI.ASTM.Document, EnsLib.EDI.EDIFACT.DocumentOpens in a new tab, and EnsLib.HL7.Message.

If you cannot make code changes, a temporary workaround is to increase the amount of memory allocated for each process. You can change this by setting the bbsiz parameter on the Advanced Memory Settings page in the Management Portal. Note that this action requires a system restart, and you should consult with your system administrator before performing it.

FeedbackOpens in a new tab