Information model query

From Endeavour Knowledge Base

Background to IMQ

Information model query language is designed to operate as an intermediary between plain language and the underlying run time query languages. The language supports query of both the information model and any health records that map to a data model as defined within the IM.

As an RDF Graph knowledge base, the information model could (and can) be directly queried using SPARQL. The IM also holds text data which can be queried directly using open Search or elastic.

However, as Health records are likely to be stored as relational, or at least SQL compatible data bases, querying health records that are aligned with the model will require SQL to query them.

Consider the following issues with SQL/SPARQL:

  • Directly authoring SQL and SPARQL languages require a high degree of skill and health query in particular needs heavily nested subqueries , including some of the more advanced techniques such as correlated query or window functions.
  • Translating a user oriented intuitive query builder into SQL or SPARQL directly and in reverse is very difficult. Most query applications use an intermediate language from which the queries are then generated. Examples include GraphQL or Power BI DAX and M.
  • Enabling direct query via SPARQL end points or SQL APIs can result in crippling performance problems and are hard to police.

Consequently the IM provides a pragmatic Query domain specific language (DSL) to help bridge the gap between a plain language representation and the run time query. This DSL, being in plain JSON, can be used to exchange query definitions across multiple instances via standard REST APIs.

The IM, as open source, also provides reference software showing how SQL or SPARQL or OpenSearch Query can be generated from the DSL and how a plain language or diagrammatic interpretation can be produced from the DSL. The reference software also shows the converse i.e. the generation of the DSL from plain language.

It is not expected that the language must be understood by clinicians, or even those familiar with SQL. Instead the purpose of the language is to enable interoperability of query definition and use across standard REST APIs. However, the logic of a query definition precisely follows the logic of a plain language description of the output required and the criteria to be applied. Thus IM query is one way of fully documenting the logic of health query, in a human and machine readable format.

The language is designed to meet the following requirements

Query language requirements

Requirement 1 - Should support the vast majority of query patterns for defining and producing data sets or patient profiles that are needed in the real world.

Requirement 2 - Should enable mapping to SQL via simple type-table, property- field maps, for health data held in relational forms, as long as the health data content conforms to an IM data model

Requirement 3 - Should enable mapping to SPARQL and Elastic and open search directly, for querying the IM itself

Requirement 4 - Should enable mapping directly from and to Snomed Expression constraint language (ECL) for searching and set definitions.

Requirement 5 - Should enable a technical query to be built as Java Script or POJO objects avoiding the need for a language specific parser.

Requirement 6- Should embed inference statements such as subtype, super type, or set inclusion as part of the query definition, thus avoiding the need for explicit modelling of the complex logic in the query itself

Requirement 7 - Should support object result format as well as relational format i.e. nested json object results as well as flat table results akin to GraphQL,

Requirement 8 - Should follow a logical plain but logical language definition of the output and criteria to be applied but there is no requirement for the language itself to be understandable by non technical users. Plain logical language is distinct from natural language, the latter being prone to ambiguity.

Requirement 9 - Should support at least one plain language and at least one diagrammatic representation of the query that can be understood by lay user.

In other words the requirement is not only to create an intermediate query model but to include a translator to and from an understandable language or diagrammatic user interface.

Language overview

The language follows the familiar pattern of most query languages with constraints. As well as incorporating the core concepts of SQL and SPARQL it includes the nested structure approach as used by GRAPHQL.

The syntax uses JSON and therefore is somewhat verbose compared with a succinct language but In return there is less ambiguity and conforms to a standard object representation.

A query consists of the following main clauses:

QueryRequest : The query run time wrapper holding the run time parameters such as reference date, paging, and contains the query either as a reference IRI to a previously authored query or the inline query itself. A Query Request contains one query

Query  : Includes the iri, name, description, result format, use of prefixes, the main entity, Select clause, sub-select clause (where a query produces many column groups). Query contains one select, but as selects contain sub-selects, complex multiple queries against a single entity type can be constructed as a single select.

Select : Equivalent to SQL and SPARQL SELECT with GraphQL nesting. Includes a list of properties and aliases as well as any nested properties i.e. select property , select and a match clause and any ordering or limit. Select contains properties to select, one or more match clauses (implied and) or one or more subset selects.

Match : Equivalent to SQL FROM/WHERE and SPARQL WHERE/FILTER. Defines the graph patterns filters or functions including ordering and limit followed by a test i.e. subquery. Match clause contains match Boolean operators (and./or/not) , and subquery, with any number of levels.

Order/ Limit/Offset  : used in both select and match, Orders by a field and optionally limits return to a number of entities. . Often used for paging but the commonest use in within a match clause to match the most recent entry of something that matches, in order to support another match on the result.

The language grammar and syntax can be represented as a formal grammar (ABNF) or Json schema. this article presents the grammar by plain language example and goes on to the more formal specification of the language.

IM Query API

The information model manager provides a REST API for querying the IM or validating health queries. As the preferred approach to API is to exchange JSON, IMQ uses the Json syntax and because entities are represented as RDF URIs, Json-LD is the preferred format.

N.B. The difference between JSON-LD and plain JSON is the representation of ontology IRIS. The JSON-LD context object enables prefixed iris for ease of reading and iri references are presented as objects with the "@id" : "http://.." format

Within the body of a query the query predicates or "key words" are represented by their local names, making it easier to map to JS or Java objects.

IMQ by example

This section takes a set of common snippets of plain language query and shows the DSL equivalent. For convenience, as JSON is somewhat bloated, quotes are omitted, and the Json-LD iri format {"@id" : "http/snomed.info/sct#12345"} is replaced with "id" the term of the IRI.

Examples start with simple patterns and progress to complex temporal patterns of the kind used in real world query.

Simple queries

Two properties of persons.

Phrase and explanation. IMQ Result (json)
Get me the NHS number and age in years of all patients.



Select things of type person,

Select their NHS number,.

Select their age, this being a "function property" that is defined in the IM as the time difference between the date of birth and a reference date, with the units of YEARS

select :{ 
     entityType :Person
     property [ {
           id : nhsNumber } ,
          {id : age,
           argument: [ { unit : YEARS }}]
   }
[ {

    nhsNumber : 1234567890,
    age : 44},

[ {
    nhsNumber : 0987654321,
    age : 14}]]

Two properties of persons, this time as CSV

IMQ Result csv)
query : {
   resultFormat= CSV
   select :{ 
         entityType :Person,
         property [ {
             id : nhsNumber } ,
            {id : age,
             argument: [ { unit : YEARS }}]

nhsNumber , age

1234567890, 44

0987654321,14


The same DSL can be used to query the IM itself

Phrase IMQ Result json
Get me the code and term of 'body structures' that match

the phrase "ches wall"

Input term "ches wall"

get the first page of length 10

that are subtypes of the concept "body structure"

query : {
    textSearch : "ches wall",
    page : 1, page size 10,
    select :{
    entityId : { id: Body structure,
                 include Subtypes : true}
     property [ { 
          id : im:code, alias : code
          iri rdf:label,alias : term}]
{[
  code : 244237006
  term : Chest wall artery },
{
   code : 78904004
   term  : chest wall structure ,

Simple cohort definition

Phrase IMQ Result json
Get me patients registered as a GMS patient with a general practice on the reference date of the query

Looking for things of type person

Looking for their GP registration entries that consist of

  1. a regular GMS patient type
  2. a start date before the reference date
  3. either no end date (still registered) or an end date after the reference date (were registered at the time)
select : {
  entityType : { id :Person}
  match : [ {
    pathTo : [ id: isSubject Of
    entityType : { id : GPRegistration}                        
    property : [ {
      id : patientType,
      isConcept : regular patient},                           
      {    
      id : effectiveDate,
      value : { 
         comparison : LESS_THAN_OR_EQUAL,          
          valueData : $referenceDate}}, 
    orProperty : [ {                                            
      notExist : true,
      id : endDate},                                           
      {
      id :endDate,
      value : {                                            
         comparison : GREATER_THAN,
         valueData : $referenceDate}}
{[
  {id : 1},
  { id : 2}]

The date range latest from pattern

Phrase IMQ Result json
Get me patients whose latest systolic blood pressure

within the last 18 months was taken in the surgery and was > 140


This is a complex double test to make sure that

a systolic blood pressure that is within 18 months prior to the reference date,

the latest of which is office based and not home based and tested to see if it is over 140


Step 1 would be to filter the observations for any systolic blood pressure, with the effective date in the time period of -18 months from the reference date

Step 2 1ould be to order them in reverse date order and select the most recent

Step 3 would be to test that it is an office based reading

Step 4 would be to test if it is greater than 140


N.B only the match clause is illustrated here. The outer select and boolean match clauses would be present in the real query.

match : {
     pathTo: [ id :is subject of,
     entityType : {id: Observation},
     property: [ {
                  id : concept,
                  inSet : Systolic arterial measurements},
                 {
                  id : effectiveDate
                  function : Time difference : 
                             argument: { [{ first date:  $this}, 
                                       { second date :referenceDate},
                                       { units : months}]},
                 value : {comparison : greater or equal, valueData -18}]
     orderBy : id :effectiveDate,
                  direction : descending,
                  count 1,
     testProperty [ {
                         id :concept,
                         in Set : Office based systolic blood pressures},
                       { 
                         value : { comparison : greater, valueData : 140}}
{[
  {id : 1},
  { id : 2}]

Expression constraint ECL


Phrase IMQ Result json
Get me concepts that are oral none steroidal ant inflammatory products


ECL for which is

<<763158003 | medinal products  :

<<127489000 : has active ingredient = <<372665008 | none steroidal anti inflammatory agent,

<<411116001 : manufactured dose form = <<385268001 | oral


Note that the "isConcept" includes subtypes by default unless indicated otherwise.



match
  entity id : { id : medicinal product, include subtypes: true
  property : [{ {id : has active ingredient, include subtypes : true},
               isConcept : non steroidal anti inflammatory agent, include subtypes : true}
              {
                {id : manufactured dose form, include subtypes true},
                is Concept : oral, include subtypes true} ]

Data Sets

Phrase IMQ Result json
Get me a data set for a cohort of patients with 120 fields grouped into 50 groups

This is a line level report from a list of patients, with data items from the health record filtered by criteria.

(Only the first couple are shown for brevity)



query : {
   select :
       mainEntity : {id : Person},
       select : { match : { entityInSet : "Cardiovascular bleed search"}}
       subselect : [{
              name : "patient details",
              property : [ {id: "fullName"},{id : age}] }
              {
              name : "Aspirin",
              pathTo : "isSubjectOf",
              property : [{id: effectiveDate},{id: medication}]}

































Grammar

This is the section on grammar