Skip to content

LifeOmic FHIR Service DSL (Domain Specific Language)

LifeOmic FHIR Service DSL enables you to perform advanced searching and advanced analytics on FHIR resources using the Elasticsearch Query DSL and Elasticsearch Aggregations APIs.

Data Types

The following table shows data type mappings between FHIR and Elasticsearch. All other data types in FHIR compose or extend the data types listed in the table below. Data types that extend the data types listed below are assumed to map to the same Elasticsearch data type unless otherwise noted.

FHIR Elasticsearch
base64Binary text / keyword
boolean boolean
date date
dateTime date
decimal numeric
instant date
integer numeric
string text / keyword
time date
uri text / keyword

Understanding Text and Keyword Data Types

Elasticsearch has two different "string" data types: text and keyword.

Each of these data types are useful in different scenarios. text fields are analyzed and are used to perform full text queries. keyword fields are used for filtering, sorting and aggregations.

In the LifeOmic FHIR Service DSL, all "string" like fields are represented as both text and keyword fields as a multi-field. The root field for every datatype is a text field and the sub-field "keyword" is a keyword field.

For example, consider the FHIR Patient identifier.value field:

In the FHIR schema, this field is a string data type. In LifeOmic FHIR Service DSL this field exists as identifier.value as a text datatype and as identifier.value.keyword as a keyword datatype.

The DSL Language

LifeOmic FHIR Service DSL is a SQL AST (Abstract Syntax Tree) and super-set of the Elasticsearch Query DSL and Elasticsearch Aggregations APIs.

Select Statement

Retrieves all FHIR resources from the targeted table.

JMES Path Definition Optional
type "select"
columns "*" or an Object Identifier
from A Table Identifier
where A Where Clause Yes
orderby An Order-By Clause Yes
limit A Limit Clause Yes

Examples

Get all Patient resources

{
    "type": "select",
    "columns": "*",
    "from": [
        {
            "table": "patient"
        }
    ]
}

Get all Patient resource id values

{
    "type": "select",
    "columns": [
        {
            "expr": {
                "type": "column_ref",
                "column": "id.keyword"
            }
        }
    ],
    "from": [
        {
            "table": "patient"
        }
    ]
}

Object Identifier

JMES Path Definition
[*] One or more Column Identifiers

or

JMES Path Definition
[0].type "elasticsearch"
[0].aggregations An Elasticsearch Aggregation

Examples

Identify multiple columns

[
    {
        "expr": {
            "type": "column_ref",
            "column": "id.keyword"
        }
    },
    {
        "expr": {
            "type": "column_ref",
            "column": "effectiveDateTime"
        }
    }
]

Count the number of id values

[
    {
        "type": "elasticsearch",
        "aggregations": {
            "results": {
                "value_count": {
                    "field": "id.keyword"
                }
            }
        }
    }
]

Estimate the number of subject.reference values

[
    {
        "type": "elasticsearch",
        "aggregations": {
            "results": {
                "cardinality": {
                    "field": "subject.reference.keyword"
                }
            }
        }
    }
]

Column Identifier

JMES Path Definition
expr.type "column_ref"
expr.column A FHIR resource field name

Table Identifier

A table identifer is a FHIR resources type written in snake_case. For example, DiagnosticReport becomes disagnostic_report.

JMES Path Definition
[0].table A FHIR resource name

Where Clause

JMES Path Definition
type "elasticsearch"
query An Elasticsearch Query DSL
highlight An Elasticsearch Highlighter

Examples

Filter where the gender is "male"

{
    "type": "elasticsearch",
    "query": {
        "term": {
            "gender.keyword": "male"
        }
    }
}

Filter where the valueInteger is greater than 10

{
    "type": "elasticsearch",
    "query": {
        "range": {
            "valueInteger": {
                "gt": 10
            }
        }
    }
}

Highlight the Diagnostic Report subject.reference

{
    "type": "select",
    "columns": "*",
    "from": [
        {
            "table": "diagnostic_report"
        }
    ],
    "where": {
        "type": "elasticsearch",
        "query": {
            "bool": {
                "must": {
                    "match": {
                        "text.div": "important"
                    }
                }
            }
        },
        "highlight": {
            "fields": {
                "text.div": {}
            }
        }
    }
}

Order-By Clause

JMES Path Definition
[*].type ASC or DESC
[*].expr A Column Identifier

Examples

Order results by the id.keyword field

{
    [
        {
            "type": "ASC",
            "expr": {
                "type": "column_ref",
                "column": "id.keyword"
            }
        }
    ]
}

Limit Clause

JMES Path Definition
[0].type "number"
[0].value The offset as a nonnegative numeric integer
[1].type "number"
[1].value The limit as a nonnegative numeric integer

or

JMES Path Definition
[0].type "elasticsearch"
[0].search_after An Elasticsearch Search After clause
[1].type "number"
[1].value The limit as a nonnegative numeric integer

Note: A Search-After clause must be used in conjunction with an Order By Clause

Examples

Limit the result to 100 results

[
  {
    "type": "number",
    "value" 0
  },
  {
    "type": "number",
    "value" 100
  }
]

Get results 11 through 20

[
  {
    "type": "number",
    "value" 10
  },
  {
    "type": "number",
    "value" 10
  }
]

Get the next 10 results after "50619f8c-10aa-464a-a227-90a7aa6ffd43"

[
  {
    "type": "elasticsearch",
    "search_after": [
        "50619f8c-10aa-464a-a227-90a7aa6ffd43"
    ]
  },
  {
    "type": "number",
    "value" 10
  }
]

Scrolling

When using scrolling, the limit clause is only recognized for the initial request. For each subsequent request, the limit is duplicated and the offset is automatically adjusted. Altering the limit clause will have no effect.

For example, if the offset and limit are set to 0 and 100, the first request will return the first 100 results (1 - 100), and the next request will return the next 100 results (101 - 200).

Scrolling is not compatible with the Elasticsearch Search-After clause.

Response

The response is a standard Elasticsearch Search Response Body. The following is a breakdown of the individual fields in the object described using JMES Path notation.

JMES Path Definition
took The amount of time it took the server to respond.
timed_out true if the server timed out, false otherwise.
shards An overview of the shards scanned.
shards.total The number of shards scanned. Additional shards can be requested by contacting LifeOmic.
shards.successful The number of shards scanned successfully.
shards.skipped The number of shards skipped.
shards.failed The number of shards scanned that failed.
hits An overview of the FHIR resources found.
hits.total The total number of FHIR resources found during scanning.
hits.total.value The total number of FHIR resources found during scanning.
hits.total.relation eq if hits.total is less than 10,000, gte otherwise.
hits.max_score The maximum relative relevancy.
hits.hits The resources returned.
hits.hits[*]._index The index the FHIR resources belong to.
hits.hits[*]._type Always _doc.
hits.hits[*]._id A Base64 encoded Resource LRN.
hits.hits[*]._score The FHIR resource relevancy.
hits.hits[*]._source The FHIR resource, whose schema matches the official FHIR schema defined by the FHIR specification. A full list of FHIR resources can be found at the FHIR Resource List.
aggregations The aggregation results. See the Elasticsearch Aggregations documentation for additional information.

Last update: June 1, 2020