cql

package module
v0.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 13, 2024 License: Apache-2.0 Imports: 11 Imported by: 0

README

Experimental Clinical Quality Language Engine

A work in progress CQL execution engine for analyzing FHIR healthcare data at scale.

CQL is a domain specific language designed for querying and executing logic on healthcare data. CQL excels at healthcare data analysis such as defining quality measures, clinical decision support, cohorts or preparing data for dashboards. CQL is designed for healthcare with first class support for terminologies, easy querying of FHIR via FHIRPath, graceful handling of mixed precision or missing data and built in clinical helper functions. You can find an intro to the CQL Language at https://cql.hl7.org.

Features

Notable features of this engine include:

  • Built in explainability - for each CQL expression definition we produce a tree that traces through the data and expressions involved in calculating the final result
  • Built in custom CQL parser, reducing project dependencies and allowing optimizations between the parser and interpreter
  • Benchmarked and optimized to be fast and memory efficient

In addition to the engine this repository has several tools that make it easy to launch and productionize CQL:

  • A scalable Beam job for running CQL over large patient populations
  • A CLI for easy configuration
  • Integration into Google Cloud Storage

Limitations

This CQL Engine is experimental and not an officially supported Google Product. The API to call the engine and the format of the results returned are subject to change. There is limited support of certain parts of the CQL Language:

  • Only the FHIR version 4.0.1 data model is supported
  • Only the Patient Context is supported
  • Not all system operators are supported
  • No support for Quantities with UCUM units
  • No support for Interval/List Promotion and Demotion
  • No support for related context retrieves
  • No support for uncertainties
  • No support for importing or exporting ELM

Getting Started

There are several different ways to use this engine. For quick experimentation we have a Command Line Interface and REPL. For executing CQL over a large patient population there is a Beam job. Finally, the CQL Golang Module allows you to execute CQL by implementing your own connector to a database and terminology server.

⚠️⚠️ Warning ⚠️⚠️

When using these tools with protected health information (PHI), please be sure to follow your organization's policies with respect to PHI.

CLI

When intending to run the CQL engine locally over small populations or for quick experimentation use the CLI located at cmd/cli. For documentation and examples see cmd/cli/README.md.

Apache Beam Pipeline

The Beam pipeline is recommended when running CQL over large patient populations. More information and usage examples are documented at beam/README.md.

Golang Module

The engine can be used via the CQL golang module documented in the godoc. The Retriever interface can be implemented to connect to a custom database or FHIR server. The Terminology Provider interface can be implemented to connect to a custom Terminology server.

REPL

For quick experiments with our CQL Engine we have a REPL. More information and usage examples are documented at cmd/repl/README.md.

Documentation

If you are interested in how this engine was implemented see docs/implementation.md for an overview of the codebase.

Documentation

Overview

Package cql provides tools for parsing and evaluating CQL.

Example

This example demonstrates the CQL API by finding Observations that were effective during a measurement period.

package main

import (
	"context"
	"fmt"
	"time"

	log "github.com/golang/glog"
	"github.com/google/cql"
	"github.com/google/cql/result"
	"github.com/google/cql/retriever/local"
	"github.com/lithammer/dedent"
)

// This example demonstrates the CQL API by finding Observations that were effective during a
// measurement period.
func main() {
	// CQL can run on different data models such as FHIR or QDM. The data model defines the CQL Named
	// types available in retrieves, their properties, subtyping and more. The parser always includes
	// the system data model, but the model info files of other data models can be provided. In this
	// example we use the FHIR data model so we can retrieve FHIR Observations and access their
	// effective and id properties. We currently only support FHIR version 4.0.1 data model.
	//
	// FHIR Helpers is a CQL Library with helper functions to covert between
	fhirDataModel, fhirHelpers, err := cql.FHIRDataModelAndHelpersLib("4.0.1")
	if err != nil {
		log.Fatal(err)
	}

	// In this example we are returning a list of the ID's of Observations that were effective during
	// the measurement period.
	libs := []string{
		dedent.Dedent(`
		library Example version '1.2.3'
		using FHIR version '4.0.1'
		include FHIRHelpers version '4.0.1'
		parameter MeasurementPeriod Interval<DateTime>
		context Patient

		define EffectiveObservations: [Observation] O where O.effective in MeasurementPeriod return O.id.value
		define FirstObservation: First(EffectiveObservations)
		`),
		fhirHelpers,
	}

	// TODO(b/335206660): Golang contexts are not yet properly supported by our engine.
	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
	defer cancel()

	// Parameters override the values of the parameters defined in the CQL library. Parameters are a
	// map from the library/parameter name to a string CQL literal. Any valid CQL literal syntax will
	// be accepted, such as 400 or List<Choice<Integer, String>>{1, 'stringParam'}. In this example we
	// override the MeasurementPeriod parameter to an interval between 2017 and 2019.
	parameters := map[result.DefKey]string{
		result.DefKey{
			Library: result.LibKey{Name: "Example", Version: "1.2.3"},
			Name:    "MeasurementPeriod",
		}: "Interval[@2017, @2019]"}

	// Parse will validate the data models and parse the libraries and parameters. ELM (which stands
	// for Expression Logical Model) holds the parsed CQL, ready to be evaluated. Anything in the
	// ParseConfig is optional.
	elm, err := cql.Parse(ctx, libs, cql.ParseConfig{DataModels: [][]byte{fhirDataModel}, Parameters: parameters})
	if err != nil {
		log.Fatalf("Failed to parse: %v", err)
	}

	for _, id := range []string{"PatientID1", "PatientID2"} {
		// The retriever is used by the interpreter to fetch FHIR resources on each CQL
		// retrieve. In this case we are in the `context patient` and call `[Observation]` so the
		// retriever will fetch all Observations for the particular Patient.
		retriever, err := NewRetriever(id)
		if err != nil {
			log.Fatalf("Failed to build retriever: %v", err)
		}

		// Eval executes the ELM (aka parsed CQL) against this particular instantiation of the
		// retriever. Anything in EvalConfig is optional.
		results, err := elm.Eval(ctx, retriever, cql.EvalConfig{})
		if err != nil {
			log.Fatalf("Failed to evaluate: %v", err)
		}

		// The results are stored in maps, and can be accessed via [result.LibKey][Definition]. The CQL
		// string, list, integers... are stored in result.Value and can be converted to a golang value
		// via GolangValue() or by passing the result.Value to a helper like result.ToString. Another
		// option is to use MarshalJSON() to convert the result.Value to json, see the results package
		// for more details.
		cqlObservationID := results[result.LibKey{Name: "Example", Version: "1.2.3"}]["FirstObservation"]

		if result.IsNull(cqlObservationID) {
			fmt.Printf("ID %v: null\n", id)
		} else {
			golangStr, err := result.ToString(cqlObservationID)
			if err != nil {
				log.Fatalf("Failed to get golang string: %v", err)
			}
			fmt.Printf("ID %v: %v\n", id, golangStr)
		}
	}

}

func NewRetriever(patientID string) (*local.Retriever, error) {
	Patient1Bundle := `{
		"resourceType": "Bundle",
		"type": "transaction",
		"entry": [
			{
				"fullUrl": "fullUrl",
				"resource": {
					"resourceType": "Patient",
					"id": "PatientID1",
					"name": [{"given":["John", "Smith"], "family":"Doe"}]}
			},
			{
				"fullUrl": "fullUrl",
				"resource": {
					"resourceType": "Observation",
					"id": "Observation1",
					"effectiveDateTime": "2012-04-02T10:30:10+01:00"
				}
			}
		 ]
	}`
	Patient2Bundle := `{
		"resourceType": "Bundle",
		"type": "transaction",
		"entry": [
			{
				"fullUrl": "fullUrl",
				"resource": {
					"resourceType": "Patient",
					"id": "PatientID2",
					"name": [{"given":["Jane", "Smith"], "family":"Doe"}]}
			},
			{
				"fullUrl": "fullUrl",
				"resource": {
					"resourceType": "Observation",
					"id": "Observation2",
					"effectiveDateTime": "2018-04-02T10:30:10+01:00"
				}
			}
		 ]
	}`
	switch patientID {
	case "PatientID1":
		return local.NewRetrieverFromR4Bundle([]byte(Patient1Bundle))
	case "PatientID2":
		return local.NewRetrieverFromR4Bundle([]byte(Patient2Bundle))
	default:
		return nil, fmt.Errorf("invalid patient id %v", patientID)
	}
}
Output:

ID PatientID1: null
ID PatientID2: Observation2

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func FHIRDataModel

func FHIRDataModel(version string) ([]byte, error)

FHIRDataModel returns the model info xml file for a FHIR data model. Currently only version 4.0.1 is supported.

func FHIRDataModelAndHelpersLib

func FHIRDataModelAndHelpersLib(version string) (fhirDM []byte, fhirHelpers string, err error)

FHIRDataModelAndHelpersLib returns the model info xml file for a FHIR data model and the FHIRHelpers CQL library. Currently only version 4.0.1 is supported.

func FHIRHelpersLib

func FHIRHelpersLib(version string) (string, error)

FHIRHelpersLib returns the FHIRHelpers CQL library. Currently only version 4.0.1 is supported.

Types

type ELM

type ELM struct {
	// contains filtered or unexported fields
}

ELM is the parsed CQL, ready to be evaluated.

func Parse

func Parse(ctx context.Context, libs []string, config ParseConfig) (*ELM, error)

Parse parses CQL libraries into our internal ELM like data structure, which can then be evaluated. Errors returned by Parse will always be a result.EngineError.

func (*ELM) Eval

func (e *ELM) Eval(ctx context.Context, retriever retriever.Retriever, config EvalConfig) (result.Libraries, error)

Eval executes the parsed CQL against the retriever. The retriever is the interface through which the interpreter retrieves external data. So if for example executing the parsed CQL against a list of patients, Eval can be called once for each patient with a retriever initialized to retrieve data for that patient. To connect to a particular data source you will need to implement the retriever.Retriever interface, or use one of the included retrievers. See the retriever package for more details. The retriever can be nil if the CQL does not fetch external data. Eval should not be called from multiple goroutines on a single *ELM. Errors returned by Eval will always be a result.EngineError.

type EvalConfig

type EvalConfig struct {
	// Terminology is the interface through which the interpreter connects to terminology servers. If
	// the CQL being evaluated does not require a terminology server this can be left nil. To connect
	// to a terminology server you will need to implement the terminology.Provider interface, or use
	// one of the included terminology providers. See the terminology package for more details.
	Terminology terminology.Provider

	// EvaluationTimestamp is the time at which the eval request will be executed. The timestamp is
	// used by CQL system operators like Today() and Now(). If not provided EvaluationTimestamp will
	// default to time.Now() called at the start of the eval request.
	EvaluationTimestamp time.Time

	// ReturnPrivateDefs if true will return all private definitions in result.Libraries. By default
	// only public definitions are returned.
	ReturnPrivateDefs bool
}

EvalConfig configures the interpreter to evaluate ELM to final CQL Results.

type ParseConfig

type ParseConfig struct {
	// DataModels are the xml model info files of the data models that will be used by the parser and
	// interpreter. The system model info is included by default. DataModels are optional and could be
	// nil in which case the CQL can only use the system data model.
	DataModels [][]byte

	// Parameters map between the parameters DefKey and a CQL literal. The DefKey specifies the
	// library and the parameters name. The CQL Literal cannot be an expression definition, valueset
	// or other CQL construct. It cannot reference other definitions or call functions. It is parsed
	// at Term in the CQL grammar. Examples of parameters could be 100, 'string',
	// Interval[@2013-01-01T00:00:00.0, @2014-01-01T00:00:00.0) or {1, 2}. Parameters are optional and
	// could be nil.
	Parameters map[result.DefKey]string
}

ParseConfig configures the parsing of CQL to our internal ELM like data structure.

Directories

Path Synopsis
Beam pipeline for computing CQL at scale.
Beam pipeline for computing CQL at scale.
transforms
Package transforms provides Dofns for Beam jobs processing CQL.
Package transforms provides Dofns for Beam jobs processing CQL.
cmd
cli
A CLI for interacting with the CQL engine.
A CLI for interacting with the CQL engine.
repl
The repl is a tool that can be used to interact with the CQL engine iteratively.
The repl is a tool that can be used to interact with the CQL engine iteratively.
internal
convert
Package convert is responsible for all things related to implicit conversions.
Package convert is responsible for all things related to implicit conversions.
datehelpers
Package datehelpers provides functions for parsing CQL date, datetime and time strings.
Package datehelpers provides functions for parsing CQL date, datetime and time strings.
embeddata
Package embeddata holds embedded data required by the production CQL engine.
Package embeddata holds embedded data required by the production CQL engine.
iohelpers
Package iohelpers contains functions for file I/O both locally and in GCS.
Package iohelpers contains functions for file I/O both locally and in GCS.
modelinfo
Package modelinfo provides utilities for working with CQL ModelInfo XML files.
Package modelinfo provides utilities for working with CQL ModelInfo XML files.
reference
Package reference handles resolving references across CQL libraries and locally within a library for the CQL Engine parser and interpreter.
Package reference handles resolving references across CQL libraries and locally within a library for the CQL Engine parser and interpreter.
resourcewrapper
Package resourcewrapper provides helper methods to work with R4 FHIR Resources.
Package resourcewrapper provides helper methods to work with R4 FHIR Resources.
testhelpers
Package testhelpers is an internal package providing useful test helpers for the CQL engine project.
Package testhelpers is an internal package providing useful test helpers for the CQL engine project.
Package interpreter interprets and evaluates the data model produced by the CQL parser.
Package interpreter interprets and evaluates the data model produced by the CQL parser.
Package model provides an ELM-like data structure for an intermediate representation of CQL.
Package model provides an ELM-like data structure for an intermediate representation of CQL.
Package parser offers a CQL parser that produces an intermediate ELM like data structure for evaluation.
Package parser offers a CQL parser that produces an intermediate ELM like data structure for evaluation.
protos
Package result defines the evaluation results that can be returned by the CQL Engine.
Package result defines the evaluation results that can be returned by the CQL Engine.
Package retriever defines the interface between the CQL engine and the data source CQL will be computed over.
Package retriever defines the interface between the CQL engine and the data source CQL will be computed over.
gcs
Package gcsretriever is an implementation of the Retriever Interface for the CQL Engine that pulls bundles from gcs.
Package gcsretriever is an implementation of the Retriever Interface for the CQL Engine that pulls bundles from gcs.
local
Package local is an implementation of the Retriever Interface for the CQL engine.
Package local is an implementation of the Retriever Interface for the CQL engine.
Package terminology includes various TerminologyProviders for working with medical terminology.
Package terminology includes various TerminologyProviders for working with medical terminology.
tests
enginetests
Package enginetests holds the most of the unit tests for the CQL Engine parser and interpreter.
Package enginetests holds the most of the unit tests for the CQL Engine parser and interpreter.
spectests/cmd/analyzer
XML Analyzer is a CLI for analyzing engine capabilities vs the external XML tests.
XML Analyzer is a CLI for analyzing engine capabilities vs the external XML tests.
spectests/exclusions
Package exclusions contains the test group and test name exclusions for the XML tests.
Package exclusions contains the test group and test name exclusions for the XML tests.
spectests/models
Code generated by https://github.com/gocomply/xsd2go; DO NOT EDIT.
Code generated by https://github.com/gocomply/xsd2go; DO NOT EDIT.
spectests/third_party/cqltests
Package cqltests contains the XML tests from the CQL specification
Package cqltests contains the XML tests from the CQL specification
Package types holds a representation of CQL types and related logic.
Package types holds a representation of CQL types and related logic.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL