Write commands
- Last UpdatedAug 26, 2026
- 12 minute read
Write commands are used to emit output to a destination. Multiple write commands may be used within a pipeline. The WriteCommand is a filter, it also outputs the input record to the next FilterCommand.
The following diagram shows an illustration of Write Commands.

Writing records
Writing records is accomplished through the WriteCommand, which writes records out to an endpoint. Several writers may exist in a single pipeline since a writer is a type of filter and so passes objects along to the next filter in the pipeline as well as writing out to the endpoint. Output from the writer are the same as the input, unless metadata is pinned upon output.
The following table lists the write command parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Endpoint |
N/A |
Begin |
[SettingRequired] The write endpoint where records are written to |
Some WriteEndpoints require WriteFormat to be specified (for example, Write-File must specify EIWM, TEXT and so on), whereas others do not require this (for example, Write-SqlServer).
A write endpoint such as a WriteFileEndpoint usually sorts input into an order suitable for efficient output (to avoid writing many files at the same time). This sorting is completely internal, however, outputs from any filter will be in the same order they came in, unless that filter specifically changes that order for the stream (such as a Sort).
Write endpoints
File
The file endpoint writes records out to 0-n files.
Folder and file are evaluated to determine if writing to a single file or many files, and the output format will help determine whether all fields need to be identified before data may be written out. A writing strategy will be determined as appropriate to the expected output, and to maintain efficiency this endpoint will work out enough information to write out entire files sequentially rather than skipping from file to file. This means it will not open many file handles and will avoid associated negative disk performance impact.
The following table lists the write file endpoint parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Folder |
IExpression <string> |
Execute |
[SettingRequired] Specifies the output folder. Since this is evaluated in the execute phase, the expression may use data to determine the path. If an output folder does not exist, it will be created just before the first file needs to be written. |
|
File |
IExpression <string> |
Execute |
[SettingRequired] Specifies the output file to write. Since this is evaluated in the execute phase, the expression may use data to determine the file name. File names will be URI encoded so for example, a / in a file name will be encoded and will not create a folder. Windows prevents some filenames like CON, LPT1, and so on. – if this occurs the value will be suffixed with an underscore ('_'). |
|
Format |
WriteFormat Config |
[SettingRequired] The format to write. |
|
|
Write-All-Headers |
IExpression <bool> |
Execute |
[SettingDefault(false)] If true then each file will include all possible fields across all records instead of only the fields applicable to that file. Since this is evaluated at execute phase, different output files may have different rules. |
|
Clean-Target-Folder |
IExpression <bool> |
Execute |
[SettingDefault(false)] If true then the contents of any target folder written to by this filter will be emptied on the first write to that folder. The folder is not deleted, only the content is cleared. |
When processing a larger set of data, too large for the buffer size of the write file operation to hold, data will be serialised in memory, and subsequently to disk if the memory store limit is exceeded.
In general, a memory store limit of 50 MB is expected to be enough for 1 million tags with 50 attributes data, since data is compressed.
Write to several folders and files by rules
Example:
<write>
<file folder="output\{{ @area_.fallback('general') }}\{{ @discipline.fallback('general') }}"
file="{{ @class.fallback('unknown') }}.csv">
<text template="csv" />
</file>
</write>
Creates one folder per area, beneath that one folder per discipline, and within each discipline folder one CSV file per class, with the specified default values where area, discipline or class is not available.
This can be used with a read command to read many EIWM files and convert them to this output.
The following table lists the write file endpoint templates.
|
Template |
Description |
|---|---|
|
Replace-Folder |
Replaces folder contents for any folder written to |
SQL Server
The SQL Server endpoint performs bulk updates to 0-n SQL database tables.
The following table lists the write SQL server endpoint parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Connection-String |
IExpression <string> |
Begin |
[SettingRequired] The database connection string to use to connect to the SQL Server database, for example: Server=MSSQLSERVER;Databa se=MYDATABASE; Trusted_Connection=True; |
|
Target-Table |
IExpression <string> |
Begin |
[SettingRequired] The table or tables to update, for example, ism_{{@Concept.toLower }}_attributes |
|
Keys |
KeyFieldsConfig |
Begin |
[SettingRequired] 0-N key fields used to uniquely identify a record in the database. If no key fields are specified, all data fields will be used as a key (hence, there must be at least 1 data field in this scenario). |
|
Date-Created-Field |
IExpression <string> |
Begin |
[SettingOptional] If specified, indicates a field in the table to populate with a created date when a record is inserted. If the specified field does not exist in the target table, this will be ignored. |
|
Date-Updated-Field |
IExpression <string> |
Begin |
[SettingOptional] If specified, indicates a field in the table to populate with an updated date when a record is inserted or updated. If the specified field does not exist in the target table, this will be ignored. |
|
Update-Timestamp |
IExpression <Datetime> |
Begin |
[SettingOptional] If set, specifies a timestamp to put in a Date-Created-Field or Date-Updated-Field. If unset, the current time will be used. |
|
Timeout |
IExpression <uint> |
Begin |
[SettingDefault(300)] Seconds until an SQL operation times out. Note: that each batch upload is a statement (generally completes within milliseconds), so this is primarily for the main SQL MERGE UPDATE statement which is executed after all data has been uploaded into #temp tables. |
|
When-Table-Missing |
IExpression <SqlTableMissingHandling> |
Begin |
[SettingDefault(Default)] 2.6.4+ What action to take if the target table does not exist.
|
|
Scope |
ScopeConfig |
Begin |
[SettingOptional] This will allow a scoped update (update to a subset of a table rather than an entire table). Not yet implemented. |
|
Update-Strategy |
IExpression <SqlUpdateStrat egy> |
Begin |
[SettingDefault(Default)] The default option here is most suitable for a production environment and general use. Default
Update
Truncate
ReplaceTable
|
The following table lists the KeyFieldsConfig Parameters in detail.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Key |
KeyFieldConfig[] |
Begin |
[SettingRequired] 1-n keys to uniquely identify a table record, which typically would be the PK of the table. |
The following table lists the KeyFieldConfig Parameters in detail.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Name |
IExpression<stri ng> |
Begin |
[SettingRequired] The name of the key column. |
Batch update SQL server using write SQL server endpoint
Example:
<write>
<sqlserver
name="Update_EDW_Rep_Attributes"
timeout="{{ $[sql-timeout-seconds] }}"
buffer-size="{{ $[sql-buffer-size] }}"
connection-string="{{ $[!reporting database] }}"
update-timestamp="{{ $[update-timestamp] }}"
target-table="EDW_{{ $[table-type] }}_Attributes"
date-created-field="CreatedDate"
date-updated-field="LastModified">
<keys>
<key name="ObjectID" />
<key name="AttributeID" />
</keys>
</sqlserver>
</write>
Following is the default method used to update:
-
Open SQL Connection.
-
For each Target-Table that results from the evaluated expression, bulk Copy N records to a #temp table (expected performance < 1 ms for 10,000 records).
-
When completed, run SQL MERGE UPDATE to update main table from #temp table.
If capturing history, capture changes records.
-
Close SQL Connection.
This should keep all SQL operations to a reasonable time.
Waypoint
Write Waypoint Endpoint updates a waypoint with received records.
When new record data is received, if the original record is not updated (that is, the new record version has the same fields and attributes as the existing record), then the existing record will not be updated.
When a record is updated in the waypoint, the last update timestamp for the record is updated.
If the same input is loaded successfully twice in a row, the second update will check existing data and determine that no changes have been made, so will perform no updates.
The following table lists the Write Waypoint Endpoint parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Folder |
IExpression <string> |
Begin |
[SettingRequired] The waypoint database folder to write to. |
|
Key |
IExpression <string> |
Execute |
[SettingRequired] Key used to uniquely identify records. |
|
Replace-Object |
IExpression <bool> |
Begin |
[SettingDefault(false)] false Merge new attributes into an existing record if a key already exists. true Replace the old object record completely if a key alredy exists. |
|
Update-Timestamp |
IExpression <DateTime> |
Begin |
[SettingDefault(now)] The timestamp to use for any actual updates. |
|
Delete |
IExpression |
Execute |
[SettingOptional] |
|
<bool> |
If set and this evaluates to true for a record, then the record will be soft deleted in the waypoint. Note: Waypoints by design have no way to completely forget information (though externally the entire waypoint repository, or a block, may be physically deleted). In this way, a waypoint always maintains a unique handle (waypoint index) for each ID it encounters. |
Records are persisted with attributes and all metadata except $[this.#temp] and $[this.waypoint.*] metadata.
If a load fails halfway through, for example, if the server crashes or is rebooted, it may have updated some data. To recover, the same load sho uld be rerun, which will detect earlier updates (and not do anything) and apply any outstanding updates.
The Waypoint update mechanism favors updates over additions, and for a managed execution environment, folder store takes out a pessimistic insert lock as soon as a new object key is identified. This may result in an insert lock of 20-30 min for 1,000,000 objects. However, update locks are taken out at a block level (a batch of for example, 10,000 objects) and updates to a block should be sub second. Hence, an update which does not add new objects should complete swiftly.
Write to a Waypoint
Example:
<read name="read_existing">
<waypoint
name="read_existing"
folder="{{$[doc_waypoint_path]}}"
include-content="false" />
<pin>
<metadata name="#sort-order" value="1!!" />
<metadata
name="id"
value="{{ coalesce($[this.id], $[this.waypoint.key]) }}" />
<metadata name="#existing" value="y" />
</pin>
</read>
<read name="read_latest">
<file
name="read_latest"
folder="{{ $[working-area] }}"
file="register_null.xml">
<eiwm />
</file>
<pin>
<metadata name="id" value="{{ @DocumentNumber }}" />
<metadata
name="#sort-order"
value="2!!{{ $[index].padnumbers(9) }}" />
<metadata name="#changed" value="y" />
</pin>
</read>
<sort key="{{ $[this.id] }}!!{{ $[this.#sort-order] }}" />
<merge
key="{{ $[this.id] }}"
int
key="{{ $[this.id] }}"
delete="{{ $[this.#changed] not equals 'y' or $[this.#deleted] equals 'y' }}"
folder="{{$[doc_waypoint_path]}}" />
</write>
Console
The write console endpoint will write records to the console standard out. This is included for testing in a console application.
This should be modified to write to the pipeline host’s UI rather than specifically the Console, which could then be used to emit output for example, into a web page.
The following table lists the write console endpoint parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Colour |
IExpression <ConsoleColor > |
Execute |
[SettingOptional] The font colour to use when writing text |
|
Background-Colour |
IExpression <ConsoleColor > |
Execute |
[SettingOptional] The background colour to use when writing text |
|
Output-Attribute-Values |
IExpression <bool> |
Begin |
[SettingDefault(false)] If set to true, individual attribute values will be written out |
|
Include-Attribute |
IExpression <bool> |
Begin |
[SettingDefault(true)] If resolves to false will prevent one or more attributes from being written out |
Write formats
This section describes about Write Formats, such as excel, binary, and text delimited.
Text delimited
Writes a delimited text file such as CSV or TAB delimited.
The following table lists the write text format parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Delimiter |
IExpression <string> |
Execute |
[SettingDefault(,)] The delimiter to use, which may be multi character. |
|
Header-Row |
IExpression <uint> |
Execute |
[SettingDefault(1)] The header row. |
|
Include-Header |
IExpression <bool> |
Execute |
[SettingDefault(true)] If false, do not include header. |
|
Quoting |
IExpression <Rfc4180Quoting> |
Execute |
[SettingDefault(WhereRequi red)] Where Required Only output quotes if required. Always Always add quotes. Off This may lead to invalid quoting, but will be useful if a CSV file is written. |
The following table provides the descriptions about the Write text format templates.
|
Template |
Description |
|---|---|
|
Tab |
Tab delimiter and header row 1. |
|
Csv |
Comma delimiter and header row 1. |
Excel
You can write the Excel (OpenXML) files with cell or row coloring or formatting options. By inserting an expression into the target folder, file and sheet names, data may be organised very flexibly.
Scenarios for use are as follows:
-
Export of data (where rules may divide data into multiple sheets such as one sheet per discipline).
-
Export to other system which accepts Excel.
Binary
The Binary format allows writing out binary content to files.
The following lists about write binary format parameters.
|
Parameter |
Value |
Phase |
Description |
|
BinaryFormat |
IExpression <BinaryStringFormat> |
NA |
Current version requires data to be stored in a text format within the pipeline - Default is BinBase64, corresponding with, for example, Read SQL Server. BinBase64|UTF8 |
|
IncludeAttribute |
IExpression<bool> |
NA |
An optional expression, which if specified will restrict which attributes get written out to the binary file format. Alternately, a SELECT could be used to restrict attributes before the WRITE. |
Extract DWG files from AVEVA P&ID database and write to folder
Example:
<pipeline id="get_drawings">
<variable name="!dbServer" value="{{ '' }}" overridable="true" />
<variable name="!dbName" value="{{ '' }}" overridable="true" />
<read>
<sql-server
connection-string="Server={{ $!dbServer }};Database={{ $!dbName }};Trusted_Connection=True;"
include-binary="true">
<query>
<![CDATA[select * from SynchroniseDetails]]>
</query>
</sql-server>
</read>
<write>
<file
folder=".\drawings_{{ $prj }}"
file="{{ @[DrawingName] }}.dwg">
<binary include-attribute="{{ $[attr.name] equals 'DrawingData' }}" />
</file>
</write>
</pipeline>
Extract DWG symbols from AVEVA P&ID and write to folder
Example:
<pipeline id="get_project_symbols">
<variable name="prj" value="OSBL" />
<read>
<sql-server
connection-string="Server=localhost;Database=SHELL_TRIDENT_PID_{{ $prj }}_ADMIN;Trusted_Connection=True;"
include-binary="true">
<query>
<![CDATA[
select * from prjSymbols
]]>
</query>
</sql-server>
</read>
<write>
<file folder=".\project_symbols_{{ $prj }}"
file="{{ @[symbol_name] }}.bmp">
<binary include-attribute="[
select * from prjSymbols>
</read>
<write>
-attribute="{{ $[attr.name] equals 'pic_binary' }}" />
</file>
</write>
<file="{{ @[symbol_name] }}.dwg">
<binary include-attribute="{{ $[attr.name] equals 'symbol_binary' }}" />
<pin>
<metadata name="filename" value="{{ $[file.name] }}" />
<metadata name="filepath" value="{{ $[file.path] }}" />
</pin>
</file>
</write>
<select>
<fields where="{{ $[attr.name] not contains 'binary' }}" />
</select>
<write>
<file
folder="."
file="project_symbols_{{ $prj }}.csv">
<text template="csv" />
</file>
</write>
</pipeline>
The EIWM template meets the standards and conventions of AIM.
For example, you want to publish OBJECT REGISTRATION details (that is, ID, Name, and Classification) without any attributes or associations. The Registers Gateway publishes attributes and associations without classification and name metadata.
The Registers Gateway requires some control over specifying dataset details, though in the most configurations this may be determined by, for example, source or some prior processing, so being able to map the data model 'dataset' for attribute values to a dataset easily will make a lot of configuration simple.
Note: This may be used without knowledge of the EIWM format.
The following table lists the write EIWM format parameters.
|
Parameter |
Value |
Phase |
Description |
|---|---|---|---|
|
Template-Id |
IExpression <string> |
Begin |
Default uses pipeline-id as this is not necessarily writing out to a file. |
|
Template-Intent |
IExpression <WriteIntent> |
Begin |
Not yet implemented |
|
Id |
IExpression <string> |
Execute |
[SettingDefault($[this.id])] |
|
Context |
IExpression <string> |
Execute |
[SettingDefault($[this.context]) ] Expression that results in a base context for the ID. By default, this is required due to the Allow- Empty-Context setting |
|
Allow-Context-In-Id |
IExpression <bool> |
Execute |
[SettingDefault(false)] If true, then IDs may include context (that is, a | pipe) |
|
Allow-Empty-Context |
IExpression <bool> |
Execute |
[SettingDefault(false)] If true, then a context is not required |
|
Name |
IExpression <string> |
Execute |
[SettingDefault($[this.name])] Name of an object, which by default will be from metadata $[this.name] |
|
Include-Name |
IExpression <bool> |
Execute |
[SettingDefault(true)] If true, then object Name (human-readable- name) should be included in output EIWM |
|
Class |
IExpression <string> |
Execute |
[SettingDefault($[this.class])] The class of an object, which by default will be from metadata $[this.class] |
|
Include-Class |
IExpression <bool> |
Execute |
[SettingDefault(true)] If true, then object class should be included in output EIWM |
|
Include-Alternate-Classes |
IExpression <bool> |
Execute |
[SettingDefault(true)] If true, then include essential and incidental class associations in output EIWM |
|
Include-Attributes |
IExpression <bool> |
Execute |
[SettingDefault(true)] expression indicating if empty attributes should be included for example, {{ $attr.name not in ['facility', 'system', 'unit'] }} |
|
Include-Empty-Attributes |
IExpression <bool> |
Execute |
[SettingDefault(true)] If true and an attribute is unset, then still include it. |
|
Dataset-Id |
IExpression <string> |
Execute |
[SettingOptional] Expression resolving to a dataset ID for an attribute. |
|
Dataset-Name |
IExpression <string> |
Execute |
[SettingOptional] Expression resolving to a dataset Name for an attribute (human-readable-name). |
|
Dataset-Class |
IExpression <string> |
Execute |
[SettingOptional] Expression resolving to a dataset Class for an attribute. |
|
Add-Dataset-Suffix |
IExpression <bool> |
Execute |
[SettingDefault(true)] |
|
Include-Object-Associations |
IExpression <bool> |
Execute |
Expression indicating if output should include associations to other objects (not applicable to datasets) |
|
Max-Content-Warnings |
IExpression <int> |
Begin |
[SettingDefault(0)] If more than 0 then error when the specified maximum number of warnings has been emitted over content. Examples of warnings are trying to write an ID without a context when Allow-Empty-Context = false. |
The following table lists the write EIWM format templates.
|
Template |
Description |
|---|---|
|
Register |
Writes EIWM with only ID, Name and Class (The VNET OBJECT REGISTER Molecular Template) Default Template-Id: source://{{ $[pipeline.Id] }}/{{ $[file.Name] }}/classes |
|
Data |
Writes EIWM with no Name or Class Default Template-Id: source://{{ $[pipeline.Id]}}/{{$[file.Name] }}/attributes |
|
All |
Writes EIWM with all available information Default Template-Id: source://{{ $[pipeline.Id]}}/{{$[file.Name] }}/full |
|
Eiwm-Register |
Writes EIWM files with only ID, Name and Class Default Template-Id: $[file.FullName].sliceOff(':',1) |