API Query Interface

When a list of results are returned, the URL ends in a / and the HTTP method is a GET, as is typical of REST design. In that case, Indivo X supports a generic query string that determines paging and ordering of the results:

?offset={offset}&limit={limit}&order_by={order_by}&status={document_status}
  • offset indicates which item number to start with, e.g. when getting a second batch of items.
  • limit indicates the maximum number of items to return. This is used in combination with offset to accomplish paging.
  • order_by is dependent on the fields returned in the list of items, and each call must thus define which fields are valid. Using an invalid field in order_by results in no effect on the output, as if order_by were absent.
  • status can be used where applicable. It pertains to the status of documents and can currently be set to one of three options: ‘void’, ‘archived’ or ‘active’

For minimal reports (url ends in /reports/minimal/{report_type}/), we expose an expanded query interface, allowing for filtering, date constraints, and aggregation. Note that this interface only makes sense to implement for minimal reports, which function solely to retrieve and display fact objects. The interface will not be implemented for any more complex report types that deal with multiple fact objects (what would it mean for a CCR report to group by lab type?). Other reports will still have access to the Beta2-style paging operations. The interface will be available for other API calls to implement as well (i.e. the Audit interface)

Output Schemas

In the previous interface, reports were templated into schemas for output as specified by the Indivo Reporting Schema. This output method will be preserved for queries that return sets of fact objects, but for queries that return aggregates or groups, we will output data according to the Indivo Aggregate Report Schema.

Data Fields

As in order_by in the Beta2 interface, each report must expose a set of data fields on which they may be filtered, grouped, or ordered. These fields can be found below.

Query Operators

Filtering Operators

  • offset, limit: Syntax is: ?offset={offset}&limit={limit}. These operators will function as previously, taking integer indexes into the result set, and returning a sliced portion of the result set from indices offset to offset + limit.
  • Custom Filters: Syntax is: ?{field}={value}. Limits result sets to items where the passed field has the passed value. If no items have such a value in the passed field, the query will return an empty result set. Field names must be data fields exposed by the desired report type.

Ordering Operators

  • order_by: Syntax is ?order_by={field}. Functions as previously. Takes a data field exposed by the desired report type, and returns the result set sorted by that field. Fields are sorted in ascending order by default, and prefixing them with a ‘-‘ will reverse the order to descending.

    Note: If order_by is used with a grouping, {field} may only refer to the field used with group_by, date_group, or aggregate_by.

Grouping and Aggregating Operators

Note: Calls using grouping and aggregating operators will return data according to the aggregation schema, not the standard query schema

  • group_by: Syntax is: ?group_by={field}. Groups result sets by the passed field, which must be a data field exposed by the desired report type. Must be used with an aggregation operator, and will throw a 400 Bad Request error otherwise. If used with order_by, the ordering field must be identical to the grouping field or the field passed in aggregate_by.
  • aggregate_by: Syntax is ?aggregate_by={operator}*{field}. Combines multiple items in a result set (or a group within a result set) into a single item using the passed operator applied to the passed field (which must be a data field exposed by the desired report type). See below for examples. Available operators are:
    • sum: returns the sum of all values of {field}. Will throw a 400 Bad Request error if the passed field does not contain numerical data.
    • avg: returns the arithmetic mean of all values of {field}. Will throw a 400 Bad Request error if the passed field does not contain numerical data.
    • max: returns the maximum value of all values of {field}. Will throw a 400 Bad Request error if the passed field does not contain numerical data or date/time data.
    • min: returns the minimum value of all values of {field}. Will throw a 400 Bad Request error if the passed field does not contain numerical data or date/time data.
    • count: returns the total number of items passed. If {field} is specified, only counts rows where <tt>{field}</tt> is not empty.

Date-based Operators

  • date_range: Syntax is ?date_range={field}*{start_date}*{end_date}. A filtering operation that limits result sets to items with values of {field} between {start_date} and {end_date} (inclusive). If either {start_date} or {end_date} is not specified, the range will be open-ended. If both are unspecified, the filter will do nothing. {field} must be a data field exposed by the desired report type. If field is not a date/time field, a 400 Bad Request error will be raised. {start_date} and {end_date} must be entered as valid UTC timestamp strings, as described in Basic Data Formats. See below for examples.

  • date_group: Syntax is: ?date_group={field}*{time_increment}. A grouping operator that, rather than grouping by a single field value, forms groups based on common increments of time. Has same restraints of use as group_by above, with the additional constraint that {field} must be a date/time data field.

    If used with order_by, the ordering field must be identical to the grouping field or the field passed in aggregate_by.

    Note: using this operator will result in the return of an aggregation schema.

    Valid increments are:

    • hour: items are placed in the same group if they occurred within the same hour.
    • day: items are placed in the same group if they occurred within the same day.
    • week: items are placed in the same group if they occurred within the same week.
    • month: items are placed in the same group if they occurred within the same month.
    • year: items are placed in the same group if they occurred within the same year.
    • hourofday: items are placed in the same group if they occurred during the same hour of day (even on separate days).
    • dayofweek: items are placed in the same group if they occurred on the same day of the week (even in separate weeks).
    • weekofyear: items are placed in the same group if they occurred during the same week of the year (indexed from 1 - 52), even in separate years.
    • monthofyear: items are placed in the same group if they occurred during the same month of the year (indexed from 1-12), even in separate years.

Query Operator Evaluation

Query operators are evaluated as follows:

  1. filter operators, including date_range but excluding limit and offset, are applied first.
  2. If group_by or date_group is passed, it is evaluated next.
  3. aggregate_by is evaluated next.
  4. order_by, limit and offset are applied.
  5. The result set is templated into the standard schema or the aggregated schema as appropriate and returned.

Notes on Aggregation

Aggregation over Indivo medical data types could be very useful in certain cases where the data is known (by an app-developer, who generated the data, say) to be highly structured. For example, consider a ‘Pedometer-Visualizer’ app, which reads in data from an electric pedometer worn by a patient, stores that data as Indivo Measurements, and displays to the patient aggregate views of their steps taken (weekly/daily averages, total miles walked, etc.). This app could take full advantage of aggregation functions such as ‘sum’, ‘avg’, etc. However, there are many cases in Indivo where the data, in spite of conforming to Indivo schemas, is not necessarily clean enough to run these aggregations. Consider the case of lab test results: the schema field is by necessity a string, as not all lab results have numerical values. Thus, an incoming query might assume that it could ask for an ‘average lab result value’, when in fact the data wouldn’t support it. We therefore cannot allow numerical aggregations over fields not explicitly labeled as ‘Number’ types (see below). If such a case is necessary for the app, the appropriate design is for the app to make a non-aggregate query, and then process the results itself (i.e., get all lab result values, and then do some data cleaning to insure that only relevant data points are counted in the averaging).

Default Operator Values

If omitted, the following query operators are assigned default values:

  • limit: 100
  • offset: 0
  • order_by: ‘-created_at’ (the date when the fact object was added to indivo). Only Applied to Non-aggregate Queries: no default ordering for aggregate queries
  • status: active

Example Queries

Below are a number of sample queries that demonstrate the power of the new interface.

Get all labs of type ‘Hematology’ within a date range

GET /records/{record_id}/reports/minimal/labs/?lab_type=Hematology&
date_range=date_measured*2009-05-04T00:00:00Z*2011-03-09T00:00:00Z

Get the number of lab results per type over the last year

GET /records/{record_id}/reports/minimal/labs/?group_by=lab_type&
aggregate_by=count*lab_test_name&date_range=date_measured*2010-03-10T00:00:00Z*

Get the number of Hematology labs per month over the last year, ordered by date

GET /records/{record_id}/reports/minimal/labs/?lab_type=Hematology&
date_group=date_measured*month&aggregate_by=count*lab_type&
order_by=-date_measured&date_range=date_measured*2010-03-10T00:00:00Z*

Valid Query Fields

Each minimal report type exposes a set of fields over which it may be queried. The fields below may be used with any query operator above in place of {field}. All exposed fields are treated as one of the following types:

  • String: a string of text. All comparisons, groups, and filters against this field will be conducted using string operations.
  • Date: an iso8601 UTC formatted datetime. All comparisons, groups, and filters against this field will be conducted using date operations.
  • Number: an integer or floating point field. All comparisons, groups, and filters against this field will be conducted using numerical operations.

If a passed query operator is inconsistent with the data type being operated on (i.e., a numerical aggregator like ‘avg’ applied to a string field like ‘lab_type’), the request will be answered with an HTTP 400 Bad Request response.

Allergies (GET /records/{RECORD_ID}/reports/minimal/allergies/)

  • date_diagnosed: The date on which the allergy was diagnosed. Date
  • allergen_type: The category of allergen causing a reaction (i.e. ‘Drugs’). String
  • allergen_name: The name of the allergen causing a reaction (i.e. ‘Penicillin’). String
  • created_at: The date on which the allergy was added to indivo. Date

Audit (GET /records/{RECORD_ID}/audits/query/)

  • document_id: The document modified by the request. String
  • external_id: The external id used to reference a resource in the request. String
  • request_date: The date on which the request was made. Date
  • function_name: The internal Indivo X view function called by the request. String
  • principal_email: The email of the principal making the request. String
  • proxied_by_email: The email of the principal proxied by the principal making the request (i.e., the email of the Account being proxied by a PHA). String

The default ordering on results will be in descending order by request_date.

Equipment (GET /records/{RECORD_ID}/reports/minimal/equipment/)

  • date_started: The date on which the patient started using the equipment. Date
  • date_stopped: The date on which the patient stopped using the equipment. Date
  • equipment_name: The name of the equipment being used. String
  • equipment_vendor: The vendor of the equipment being used. String
  • created_at: The date on which the equipment was added to indivo. Date

Immunizations (GET /records/{RECORD_ID}/reports/minimal/immunizations/)

  • vaccine_type: The type of Vaccination administered. String
  • date_administered: The date on which the patient received the vaccination. Date
  • created_at: The date on which the immunization was added to indivo. Date

Labs (GET /records/{RECORD_ID}/reports/minimal/labs/)

  • lab_type: The category of the lab. String
  • date_measured: The date on which the lab was measured. Date
  • lab_test_name: The name of the test conducted. String
  • created_at: The date on which the lab was added to indivo. Date

Measurements (GET /records/{RECORD_ID}/reports/minimal/measurements/{LAB_CODE}/)

  • lab_code: The identifier for the measurement taken. String
  • value: The value measured. Number
  • date_measured: The date on which the measurement was taken. Date
  • created_at: The date on which the measurement was added to indivo. Date

Medications (GET /records/{RECORD_ID}/reports/minimal/medications/)

  • date_started: The date on which the patient started taking the medication. Date
  • date_stopped: The date on which the patient stopped taking the medication. Date
  • medication_name: The name of the medication being taken. String
  • medication_brand_name: The brand name of the medication being taken. String
  • created_at: The date on which the medication was added to indivo. Date

Problems (GET /records/{RECORD_ID}/reports/minimal/problems/)

  • date_onset: The date on which the patient started experiencing the problem. Date
  • date_resolution: The date on which the problem was resolved. Date
  • problem_name: The problem name. String
  • created_at: The date on which the problem was added to indivo. Date

Procedures (GET /records/{RECORD_ID}/reports/minimal/procedures/)

  • date_performed: The date on which the procedure was performed. Date
  • procedure_name: The name of the procedure. String
  • created_at: The date on which the procedure was added to indivo. Date

Simple Clinical Notes (GET /records/{RECORD_ID}/reports/minimal/simple-clinical-notes/)

  • date_of_visit: The date on which the clinical note was taken. Date
  • specialty: The clinical specialty relevant to the visit. String
  • provider_name: The care provider at the visit. String
  • created_at: The date on which the clinical note was added to indivo. Date

Vitals (GET /records/{RECORD_ID}/reports/minimal/vitals/)

  • date_measured: The date on which the vital sign was measured. Date
  • category: The category of vital sign taken. String
  • value: The value measured. Number
  • created_at: The date on which the vital sign was added to indivo. Date