HL7 (elixir_hl7 v0.10.0)

View Source

Struct and functions for parsing and manipulating HL7 messages.

This library specifically handles version 2.x of HL7 as it is by far the most prevalent format in production.

Check out HL7 on Wikipedia for a decent overview of the format.

Since HL7 messages often lack critical contextual metadata, this struct also contains a tags field for metadata support.

Use new/2 or new!/2 to convert HL7 text to a parsed HL7 struct. This struct supports the String.Chars protocol such that to_string/1 can be used to format the HL7 message text.

To see the parsed representation, call get_segments/1.

To query or update HL7 data, use the sigil_p/2 macro to provide an HL7.Path with compile-time guarantees. For dynamic path access, use HL7.Path.new/1 to construct paths on the fly. Note that HL7 path formats have been designed to reflect common industry usage.

The get/2, put/3, and update/4 functions are designed to query and manipulate HL7 data as an HL7 struct (containing a message), a list of segments, a single segment, a list of repetitions, or a single repetition. These should handle data as nested lists (automatically converted to 1-indexed maps), nested sparse maps (1-indexed), simple strings, or mixes of each.

Use set_segments/2 to fully replace the content an HL7 message.

Migrating from HL7.Message, HL7.Segment and HL7.Query

To migrate from the deprecated HL7.Message struct, use HL7.new!/2 and HL7.Message.new/2 to transform from one struct to the other while preserving associated metadata tags.

You can use chunk_by_lead_segment/3 to generate segment groups to update code that relies on HL7.Query groupings.

Any operations to otherwise query or modify HL7 data should be possible using the get/2, put/3, update!/3 and update/4 functions.

If you encounter other issues with feature parity, please open an issue!

String.Chars Protocol

You can use the to_string() implementation of the String.Chars protocol to quickly render HL7 structs as text.

Summary

Functions

Creates a list of lists in which the specified segment_name is used to get the first segment map of each list. This function helps to do things like grouping OBX segments with their parent OBR segment.

Converts an HL7 struct into its string representation. Convenience options will be added in the future.

Finds data within an HL7 struct, parsed segments or repetitions using an HL7.Path struct (see sigil_p/2).

Returns a list of sparse maps (with ordinal indices and strings) representing the parsed segments of an HL7 message stored in an HL7 struct.

Returns a map of custom metadata associated with the HL7 struct.

Labels source data (a segment map or list of segment maps) by using HL7.Paths in a labeled output template.

Creates an HL7 struct from valid HL7 data (accepting text, lists or the deprecated HL7.Message struct). Returns {:ok, HL7.t()} if successful, {:error, HL7.InvalidMessage.t()} otherwise.

Creates an HL7 struct from valid HL7 data (accepting text, lists or the deprecated HL7.Message struct). Raises with a RuntimeError if the data is not valid HL7.

Creates a minimal map representing an empty HL7 segment that can be modified via this module.

Opens an HL7 file stream of either :mllp or :line. If the file_type is not specified it will be inferred from the first three characters of the file contents.

Puts data within an HL7 struct, parsed segments or repetitions using an HL7.Path struct (see sigil_p/2).

Sets a list of sparse maps (with ordinal indices and strings) representing the parsed segments of an HL7 message to define the content of an HL7 struct.

Sets a map of custom metadata associated with the HL7 struct.

The ~p sigil encodes an HL7 path into a struct at compile-time to guarantee correctness and speed. It is designed to work with data returned by HL7.new!/1, providing a standard way to get and update HL7 message content.

Converts an HL7 message struct into a nested list of strings.

Updates data within an HL7 struct, parsed segments, or repetitions using an HL7.Path struct (see sigil_p/2).

Updates data within an HL7 struct, parsed segments, or repetitions using an HL7.Path struct (see sigil_p/2). Raises a RuntimeError if the path is not present in the source data.

Types

file_type_hl7()

@type file_type_hl7() :: :mllp | :line | nil

hl7_list_data()

@type hl7_list_data() :: String.t() | [hl7_list_data()]

hl7_map_data()

@type hl7_map_data() :: %{optional(non_neg_integer()) => hl7_map_data() | String.t()}

parsed_hl7()

@type parsed_hl7() :: t() | segment() | [segment()] | hl7_map_data()

segment()

@type segment() :: %{
  required(0) => String.t(),
  optional(pos_integer()) => hl7_map_data() | String.t()
}

t()

@type t() :: %HL7{segments: [segment()], tags: map()}

Functions

chunk_by_lead_segment(segments, segment_name, options \\ [])

@spec chunk_by_lead_segment(t() | [segment()], String.t(), Keyword.t()) :: [
  [segment()]
]

Creates a list of lists in which the specified segment_name is used to get the first segment map of each list. This function helps to do things like grouping OBX segments with their parent OBR segment.

Options:

keep_prefix_segments: true will leave the first set of non-matching segments in the return value.

format(hl7, options \\ [])

Converts an HL7 struct into its string representation. Convenience options will be added in the future.

get(data, path)

@spec get(parsed_hl7(), HL7.Path.t()) ::
  hl7_map_data() | [hl7_map_data()] | String.t() | nil

Finds data within an HL7 struct, parsed segments or repetitions using an HL7.Path struct (see sigil_p/2).

Selecting data across multiple segments or repetitions with the wildcard [*] pattern will return a list of results.

Repetition data can be searched using a partial path containing ony the component and/or subcomponent with a preceding period, e.g. ~p".2.3".

See the sigil_p/2 docs and tests for more examples!

Examples

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> HL7.new!()
...> |> HL7.get(~p"OBX-5")
"1.80"

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> HL7.new!()
...> |> HL7.get(~p"OBX[*]-5")
["1.80", "79"]

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> HL7.new!()
...> |> HL7.get(~p"OBX[*]-2!")
["N", "NM"]

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> HL7.new!()
...> |> HL7.get(~p"PID-11[*].5")
["35209", "35200"]

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> HL7.new!()
...> |> HL7.get(~p"PID-11[2].1")
"NICKELL’S PICKLES"

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> get(~p"PID")
...> |> get(~p"11[*]")
...> |> get(~p".1")
["260 GOODWIN CREST DRIVE", "NICKELL’S PICKLES"]

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"OBX[*]")
[
  %{
    0 => "OBX",
    1 => "1",
    2 => %{1 => %{1 => "N", 2 => %{1 => "K", 2 => "M"}}},
    3 => %{1 => %{2 => "Body Height"}},
    5 => "1.80",
    6 => %{1 => %{1 => "m", 2 => "Meter", 3 => "ISO+"}},
    11 => "F"
  },
  %{
    0 => "OBX",
    1 => "2",
    2 => "NM",
    3 => %{1 => %{2 => "Body Weight"}},
    5 => "79",
    6 => %{1 => %{1 => "kg", 2 => "Kilogram", 3 => "ISO+"}},
    11 => "F"
  }
]

get_segments(hl7)

Returns a list of sparse maps (with ordinal indices and strings) representing the parsed segments of an HL7 message stored in an HL7 struct.

get_tags(hl7)

Returns a map of custom metadata associated with the HL7 struct.

label(segment_or_segments, template_map)

@spec label(t() | segment(), map()) :: map()

Labels source data (a segment map or list of segment maps) by using HL7.Paths in a labeled output template.

One-arity functions placed as output template values will be called with the source data.

Examples

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> HL7.new!()
...> |> HL7.label(%{mrn: ~p"PID-3!", name: ~p"PID-5.2"})
%{mrn: "56782445", name: "BARRY"}

new(text, options \\ [])

@spec new(String.t(), Keyword.t()) :: {:ok, t()} | {:error, HL7.InvalidMessage.t()}

Creates an HL7 struct from valid HL7 data (accepting text, lists or the deprecated HL7.Message struct). Returns {:ok, HL7.t()} if successful, {:error, HL7.InvalidMessage.t()} otherwise.

new!(message_content, options \\ [])

@spec new!(list() | String.t() | HL7.Message.t(), Keyword.t()) :: t()

Creates an HL7 struct from valid HL7 data (accepting text, lists or the deprecated HL7.Message struct). Raises with a RuntimeError if the data is not valid HL7.

Examples

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!()
#HL7<with 8 segments>

new_segment(segment_name)

@spec new_segment(String.t()) :: segment()

Creates a minimal map representing an empty HL7 segment that can be modified via this module.

open_hl7_file_stream(file_path, file_type \\ nil)

@spec open_hl7_file_stream(String.t(), file_type_hl7()) :: Enumerable.t()

Opens an HL7 file stream of either :mllp or :line. If the file_type is not specified it will be inferred from the first three characters of the file contents.

put(hl7, path, value)

@spec put(parsed_hl7(), HL7.Path.t(), String.t() | nil | hl7_map_data()) ::
  parsed_hl7()

Puts data within an HL7 struct, parsed segments or repetitions using an HL7.Path struct (see sigil_p/2).

Examples

Put field data as a string.

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> put(~p"PID-8", "F")
...> |> get(~p"PID-8")
"F"

Put field data as a string overwriting all repetitions.

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> put(~p"PID-11[*]", "SOME_ID")
...> |> get(~p"PID-11[*]")
["SOME_ID"]

Put field data into a single repetition.

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> put(~p"PID-3[2]", ["a", "b", "c"])
...> |> get(~p"PID-3[2].3")
"c"

Put component data across multiple repetitions.

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> put(~p"PID-11[*].3", "SOME_PLACE")
...> |> get(~p"PID-11[*].3")
["SOME_PLACE", "SOME_PLACE"]

Put data in a segment using just the path to a field.

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> get(~p"PID")
...> |> put(~p"3", "SOME_ID")
...> |> get(~p"3")
"SOME_ID"

Put data across multiple segments

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> put(~p"OBX[*]-5", "REDACTED")
...> |> get(~p"OBX[*]-5")
["REDACTED", "REDACTED"]

set_segments(hl7, segments)

Sets a list of sparse maps (with ordinal indices and strings) representing the parsed segments of an HL7 message to define the content of an HL7 struct.

set_tags(hl7, tags)

Sets a map of custom metadata associated with the HL7 struct.

sigil_p(arg, modifiers)

(macro)

The ~p sigil encodes an HL7 path into a struct at compile-time to guarantee correctness and speed. It is designed to work with data returned by HL7.new!/1, providing a standard way to get and update HL7 message content.

Importing Just the Sigil

Use import HL7, only: :sigils to access the ~p sigil without importing the other HL7 functions.

The full path structure in HL7 is expressed as:

~p"SEGMENT_NAME[SEGMENT_NUMBER]-FIELD[REPETITION].COMPONENT.SUBCOMPONENT

A trailing exclamation mark can be used in the path to the return only the leftmost text at the given level.

Position NameValid Values
SEGMENT_NAME3 character string
SEGMENT_NUMBERPositive integer in square brackets, defaults to 1. All segments of SEGMENT_NAME can be accessed with [*]
FIELDPositive integer
REPETITIONPositive integer in square brackets, defaults to 1. All repetitions of the FIELD can be accessed with [*]
COMPONENTPositive integer
SUBCOMPONENTPositive integer

1-based Indexes

To match industry expectations, HL7 uses 1-based indexes. As noted above, it also includes defaults whereby all paths refer to the 1st segment and/or 1st repetition of any query unless explicitly specified.

Example paths:

HL7 PathDescription
~p"OBX"The 1st OBX segment in its entirety, the same as ~p"OBX[1]".
~p"OBX-5"The 1st repetition of the 5th field of the 1st OBX segment.
~p"OBX[1]-5[1]"Same as above. The numbers in brackets specify the default repetition and segment number values.
~p"OBX-5[*]"Every repetition of the 5th field of the 1st OBX segment, returning a list of results.
~p"OBX[2]-5"The 1st repetition of the 5th field of the 2nd OBX segment.
~p"OBX[*]-5"The 1st repetition of the 5th field of every OBX segment, returning a list of results.
~p"OBX[*]-5[*]"Every repetition of the 5th field of every OBX segment, returning a nested list of results.
~p"OBX-5.2"The 2nd component of the 1st repetition of the 5th field of the 1st OBX segment.
~p"OBX-5.2.3"The 3rd subcomponent of the 2nd component of the 1st repetition of the 5th field of the 1st OBX segment.
~p"OBX[*]-5.2.3"Same as above, but returned as a list with a value for each OBX segment.
~p"OBX[*]-5[*].2.3"Same as above, but now a nested list of results for each repetition within each segment.
~p"OBX-5!"The ! will take the first text (leftmost value) at whatever level is specified. Thus, p"OBX-5!" is equivalent to p"OBX-5.1.1".
~p"5"The fifth field of a segment (parsed as an ordinal map starting with 0)
~p".2"The second component of a repetition (parsed as an ordinal map starting with 1)

Note that repetitions are uncommon in HL7 and the default of a 1st repetition is often just assumed. ~p"PID-3" is equivalent to ~p"PID-3[1]" and is the most standard representation.

All repetitions can be found using a repetition wildcard: ~p"PID-11[*]". A list of lists can be produced by selecting multiple segments and multiple repetitions with ~p"PID[*]-11[*]".

Components and subcomponents can also be accessed with the path structures. p"OBX-2.3.1" would return the 1st subcomponent of the 3rd component of the 2nd field of the 1st OBX.

If dealing with segments or repetitions extracted from parsed HL7, you can use partial paths that lack the segment name and/or field like ~p"5" for the fifth field of a segment or ~p".3" for the 3rd component of a repetition.

Additionally, if a path might have additional data such that a string might be found at either ~p"OBX-2" or ~p"OBX-2.1" or even ~p"OBX-2.1.1", there is truncation character (!) that will return the first element found in the HL7 text at the target specificity. Thus, ~p"OBX[*]-2!" would get the 1st piece of data in the 2nd field of every OBX whether it is a string or nested map.

Nil vs Empty String

Paths that query beyond the content of an HL7 document, e.g. asking for the 10th field of a segment with five fields, will return nil as opposed to an empty string to indicate that the data does not exist. Adding the trailing ! to an HL7.Path will force all return values to be simple strings in cases where nil is not desired. Note that it also returns the leftmost text at the path level, discarding extra data as noted above.

Examples

iex> import HL7, only: :sigils
iex> HL7.Examples.wikipedia_sample_hl7() |> HL7.new!() |> HL7.get(~p"OBX-5")
"1.80"

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"OBX-3")
%{2 => "Body Height"}

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"PID-11!")
"260 GOODWIN CREST DRIVE"

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"OBX[*]-5")
["1.80", "79"]

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"OBX[*]-2!")
["N", "NM"]

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"PID-11[*].5")
["35209", "35200"]

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"PID[*]-11[*].5")
[["35209", "35200"]]

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7() |> new!() |> get(~p"PID-11[2].1")
"NICKELL’S PICKLES"

iex> import HL7
iex> HL7.Examples.wikipedia_sample_hl7()
...> |> new!()
...> |> get(~p"PID-11[*]")
...> |> List.last()
...> |> get(~p".1")
"NICKELL’S PICKLES"

to_list(hl7)

@spec to_list(t()) :: hl7_list_data()

Converts an HL7 message struct into a nested list of strings.

update(hl7, path, default, fun)

@spec update(
  parsed_hl7(),
  HL7.Path.t(),
  String.t() | nil | hl7_map_data(),
  (hl7_map_data() ->
     hl7_map_data())
) ::
  parsed_hl7()

Updates data within an HL7 struct, parsed segments, or repetitions using an HL7.Path struct (see sigil_p/2).

update!(hl7, path, fun)

@spec update!(parsed_hl7(), HL7.Path.t(), (hl7_map_data() -> hl7_map_data())) ::
  parsed_hl7()

Updates data within an HL7 struct, parsed segments, or repetitions using an HL7.Path struct (see sigil_p/2). Raises a RuntimeError if the path is not present in the source data.