> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.emtelligent.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.emtelligent.com/_mcp/server.

# Follow-up Relations

In this section, we explain how structured data for medications relations is represented in the NLP API JSON output and in the Output Database schema. We also provide sample queries to illustrate the key aspects of the data schema.

After reading this page, we hope you will:

1. Understand what follow-up relations are.
2. Know how to retrieve data for follow-up relations.

For an example of building on this data, see also the [Simple Query App Tutorial](/nlp/database/app-tutorial), which walks through a simple web-based search application over the NLP output stored in this database.

The content in this section builds on the concepts and data models explained in earlier sections (see [Entity-level Tables](/nlp/database/schema/core/entity-tables), and [Document-level Tables](/nlp/database/schema/core/document-tables)).

## What are Follow-up Relations?

Follow-up recommendations are commonly found in diagnostic reports such as Radiology, Pathology and Cardiology reports. For example, the NLP API will extract them from sentences in radiology reports where the dictating radiologist indicates that a follow-up study should be performed. The radiologist will typically provide some or all of the following information in a follow-up recommendation:

* **Reason**: the pathology identified that should be followed up e.g. a lung nodule.
* **Procedure**: the type of diagnostic procedure that should be performed for further evaluation.
* **Time expression**: when the follow-up procedure should be performed.

The NLP API extracts these as arguments of a **follow-up relation**.

Consider this follow-up recommendation:

```text
A follow-up Head CT scan in 3 months time is recommended to exclude malignancy
```

From the above sentence, the NLP API extracts the arguments comprising the follow-up relation as illustrated below:

![Qualifier relation required and optional arguments](/_fern-img/033e87d20676ad4976dfb161d99dec3b8594745151b239932eca9164fb075d64.webp)

Note, at least one of the three types of arguments must be provided for a follow-up relation to be extracted.

## JSON Data Model

Consider the following sentence:

```text
A follow-up Head CT scan in 3 months time is recommended to exclude malignancy
```

The JSON output for the follow-up relation extracted from this sentence is shown below:

```json
{
    "relations": {
        "followups": [
            {
                "label": "RFO0",
                "attributes": {
                    "confidence": null,
                    "polarity": "asserted"
                },
                "args": {
                    "procedures": [
                        {
                            "ref": "E1",
                            "text": [
                                "Head CT scan"
                            ]
                        }
                    ],
                    "time_expression": {
                        "ref": "E2",
                        "text": [
                            "3 months"
                        ]
                    },
                    "reasons": [
                        {
                            "ref": "E3",
                            "text": [
                                "malignancy"
                            ]
                        }
                    ]
                },
                "concept_links": []
            }
        ]
    }
}
```

Notice the following:

1. The `followups` array is an element of the `relations` object. Each element of the `followups` array is a single object representing a follow-up relation.
2. A follow-up relation instance has three types of named arguments: `reasons`, `procedures` and `time_expression`.
3. The `reasons` argument is an **array of entity references** to the reasons for the follow-up recommendation. Note that multiple reasons may be associated with one follow-up relation.
4. Likewise, the `procedures` argument is an **array of references** to entities about procedures related to the follow-up. Like reasons for follow-ups, multiple procedures may be found in one follow-up relation.
5. The `time_expression` argument is a reference to an entity that indicates the timeline for the follow-up. Note that only **one** time_expression entity is extracted per follow-up relation.
6. Follow-up relations have a `polarity` attribute. The value of the **polarity** can be `negated` or `asserted`. The `negated` follow-up relation is extracted when the radiologist explicitly indicates that no follow-up is necessary.

## Follow-up Relation Tables

In the Output Database schema, the follow-up relations data model is implemented in the **followuprelation** table, and its related tables: **followuprelationreason** and **followuprelationprocedure**.

The following ER diagram shows the relationship between these tables:

![Entity-Relation diagram showing followuprelation and its associated tables.](/_fern-files/emtelligent-prod.docs.buildwithfern.com/80a71837574c95c61b6302f3515925ca5ed9280fa5335c63e6d8a3d001c1deee/assets/nlp/followuprelation_schema.png)

From the ER diagram above, observe that:

1. The **followuprelation** table stores the **polarity** attribute, and the **entity_id** of the **time_expression argument**.

2. The **time_expression_id** column of the **followuprelation** table is a foreign key reference to the **entity** table, or null. From the **entity** table you can perform a series of inner joins to access the entity information from entity-related tables, see [Entity-level Tables](/nlp/database/schema/core/entity-tables).

3. The **followup_id** columns of the **followuprelationreason** and the **followuprelationprocedure** tables contain foreign key references to the **followuprelation** table. Hence,

   > 1. to get the **reason** for a follow-up relation, perform an inner join on the primary key of the **followuprelation** and the **followup_id** of the **followuprelationreason** table.
   > 2. to get the **procedure** for a follow-up relation, perform an inner join on the primary key of the **followuprelation** and the **followup_id** of the **followuprelationprocedure** table.

4. To retrieve information about the follow-up relation arguments such as the reason or procedure, you must perform an inner join on the corresponding **entity_id** keys with the **entity** associated entity-related tables, see [Entity-level Tables](/nlp/database/schema/core/entity-tables).

It's important to note the cardinality of the relations. A follow-up relation can be associated with:

* **one or more** procedures entities
* **zero or more** reasons entities
* **zero or one** time_expression entity

> **Warning**
>
> When querying follow-up relations, it is important to note that unlike other relations, there are no independent or required arguments. The **reasons**, **procedures** or **time_expression** for follow-up relations may be `null`. This has particular significance when you are performing left joins on these tables.

## Sample Queries

In this section, we present queries to illustrate how to write SQL queries to retrieve information about qualifier relations. The SQL queries will be discussed in order as follows:

* [Query: Display terms for arguments in follow-up relations](#query-display-terms-for-arguments-in-follow-up-relations)
* [Query: Frequency of reasons for follow-up recommendations](#query-frequency-of-reasons-for-follow-up-recommendations)
* [Query: Frequency of follow-up imaging recommendations](#query-frequency-of-follow-up-imaging-recommendations)

Note: Because follow-up relations are specific to Radiology reports, please use an Output Database that contains output from processing Radiology reports.

### Query: Display terms for arguments in follow-up relations

This query retrieves the annotated terms follow-up relation arguments. This query illustrates the cardinality of the relation between a follow-up relation and its arguments.

The query involves the following steps:

1. Using a CTE, create a temporary named result set, `reason_terms`, which contains follow-up relation ID, and the annotated text for follow-up reasons. This involves the following:
   1. Joining the **followuprelation** table with the **followuprelationreason** table using **followup_id** foreign key reference. Then perform a series of inner joins with **entity** and **foundentity** tables. The annotated term is found in the **foundentity** table.
   2. Aggregating the annotated terms using a Window function to compute a comma-delimited list. At this point there will be duplicate rows for each `followuprelation.id` - one for each reason associated with a follow-up relation instance. Later, these duplicate rows will be removed in the GROUP BY clause in the enclosing query.
2. As in step one, create a temporary named result set, `procedure_terms` which contains the follow-up relation ID, and the annotated text for follow-up procedures.
3. Then, create a temporary named result set, `time_expression` which contains follow-up relation ID and the annotated text for the follow-up time expression.
4. In the enclosing SQL query, use a subquery to create a named result set, `ids`, which contains the follow-up relation primary key ID. Then, perform a series of **left joins** on the ID field of the `ids` result set and the `reason_terms`, `procedure_terms` and `time_expression` result sets. The **left** join on the `ids.followup_relation.id` ensures that all follow-up relations are present in the final results, whether or not any individual argument is `null`.

```sql
  WITH reason_terms AS (SELECT followuprelation.id AS followup_relation_id,
                               string_agg(reason_fe.text, ', ') OVER (PARTITION BY followuprelation.id) AS reason_terms
                          FROM followuprelation
                                   -- retrieve reason entity for follow up
                                   JOIN followuprelationreason reason
                                   ON followuprelation.id = reason.followup_id
                                   JOIN entity reason_e
                                   ON reason_e.id = reason.entity_id AND reason_e.type_='found'
                                   JOIN foundentity reason_fe
                                   ON reason_fe.id = reason_e.entity_id),
       time_expression_terms AS (SELECT followuprelation.id AS followup_relation_id, time_fe.text AS time_expression
                                   FROM followuprelation
                                            -- retrieve reason entity for follow up
                                            JOIN entity time_e
                                            ON time_e.id = followuprelation.time_expression_id
                                            JOIN foundentity time_fe
                                            ON time_fe.id = time_e.entity_id AND time_e.type_='found'),
       procedure_terms AS (SELECT followuprelation.id AS followup_relation_id,
                                  string_agg(procedure_fe.text, ', ')
                                  OVER (PARTITION BY followuprelation.id) AS procedure_terms
                             FROM followuprelation
                                      -- retrieve reason entity for follow up
                                      JOIN followuprelationprocedure procedure
                                      ON followuprelation.id = procedure.followup_id
                                      JOIN entity procedure_e
                                      ON procedure_e.id = procedure.entity_id AND procedure_e.type_='found'
                                      JOIN foundentity procedure_fe
                                      ON procedure_fe.id = procedure_e.entity_id)
SELECT fr.id, reason_terms.reason_terms, procedure_terms.procedure_terms,
       time_expression_terms.time_expression
  FROM     followuprelation fr
           LEFT JOIN reason_terms -- IMPORTANT: left joins required
           ON reason_terms.followup_relation_id = fr.id
           LEFT JOIN procedure_terms
           ON procedure_terms.followup_relation_id = fr.id
           LEFT JOIN time_expression_terms
           ON time_expression_terms.followup_relation_id = fr.id
 GROUP BY fr.id, reason_terms.reason_terms, procedure_terms.procedure_terms,
          time_expression_terms.time_expression
 LIMIT 6 OFFSET 12;
```

| id | reason_terms       | procedure_terms    | time_expression |
| -- | ------------------ | ------------------ | --------------- |
| 13 | obstruction        | follow-up CT, scan | —               |
| 14 | —                  | discharge          | —               |
| 15 | —                  | blood cultures     | —               |
| 16 | —                  | platelets          | 03/16/22        |
| 17 | Further evaluation | MRI                | —               |
| 18 | further evaluation | CT, MRI            | —               |

### Query: Frequency of reasons for follow-up recommendations

This query is useful as a preliminary exploration of the reasons given for follow-ups. Instead of analyzing the annotated terms which have many linguistic variants, we analyze the reasons using standardized concepts and their semantic type classification.

Because we are interested in the **reasons** for follow-ups, we start by querying the **followuprelationreason** table. Then, we can retrieve the concepts and entity type for every follow-up reason entity by performing an inner join with the **entity** table, and related tables such as **foundentityconcept** and **foundentitytype** tables. Note that NLP API supports multiple ontologies for concept and semantic type classification purposes. In this query, we use the UMLS metathesaurus for semantic type classification, and SNOMED CT for concept classification of the annotated terms.

```sql
SELECT reason_c.description AS reason_concept, 
       reason_c.ontology AS concept_ontology, reason_fet.type_name AS reason_semantic_type, reason_fet.ontology AS reason_semantic_type_ontology,
       count(*) AS count
  FROM followuprelationreason
           JOIN entity reason_e
           ON reason_e.id = followuprelationreason.entity_id AND reason_e.type_='found'
           JOIN foundentity reason_fe
           ON reason_fe.id = reason_e.entity_id
           JOIN foundentitytype reason_fet
           ON reason_fet.found_entity_id = reason_fe.id AND reason_fet.ontology = 'umls' -- Select ontology e.g. 'snomed'
           JOIN foundentityconcept reason_fec
           ON reason_fec.found_entity_id = reason_fe.id
           JOIN concept reason_c
           ON reason_c.concept_id = reason_fec.concept_id 
           AND reason_c.ontology = reason_fec.concept_ontology -- Required
 WHERE reason_c.ontology = 'snomed'  -- Select an ontology e.g. 'snomed', 'medcin', 'radlex', 'umls_nci', 'umls_loinc'
 GROUP BY reason_fe.text, reason_c.description, reason_c.ontology, reason_fet.type_name, reason_fet.ontology
 ORDER BY 1
 LIMIT 5;
```

| reason_concept                                 | concept_ontology | reason_semantic_type | reason_semantic_type_ontology | count |
| ---------------------------------------------- | ---------------- | -------------------- | ----------------------------- | ----- |
| Appointment date (finding)                     | snomed           | Finding              | umls                          | 2     |
| Atrial fibrillation (disorder)                 | snomed           | Disease or Syndrome  | umls                          | 1     |
| Barrett's esophagus (disorder)                 | snomed           | Disease or Syndrome  | umls                          | 1     |
| Cardiac enzyme above reference range (finding) | snomed           | Finding              | umls                          | 1     |
| Enchondroma of bone (disorder)                 | snomed           | Neoplastic Process   | umls                          | 1     |

### Query: Frequency of follow-up imaging recommendations

This query retrieves the count of follow-up recommendations by imaging modality. Because this query is about the procedure associated with follow-up relations, we start with the **followuprelationprocedure** table. By performing a series of inner joins with entity-related tables, we get the **concept** and **entity type** for procedure entities from the **concept** and **foundentitytype** tables respectively. Note, for this query, we used the RadLex ontology as a concept and semantic type classification system; And, we constrain the resultset to only those follow-ups where the procedure is of semantic type `imaging modality`.

```sql
SELECT proc_c.description AS procedure_concept, proc_c.ontology AS concept_ontology,
       proc_fet.type_name AS procedure_semantic_type, proc_fet.ontology AS procedure_semantic_type_ontology,
       count(*) AS count
  FROM followuprelationprocedure
           JOIN entity proc_e
           ON proc_e.id = followuprelationprocedure.entity_id AND proc_e.type_='found'
           JOIN foundentity proc_fe
           ON proc_fe.id = proc_e.entity_id
           JOIN foundentitytype proc_fet
           ON proc_fet.found_entity_id = proc_fe.id AND proc_fet.ontology = 'radlex' -- Ontology for semantic classification e.g. 'umls', 'snomed'
           JOIN foundentityconcept proc_fec
           ON proc_fec.found_entity_id = proc_fe.id
           JOIN concept proc_c
           ON proc_c.concept_id = proc_fec.concept_id AND proc_c.ontology = proc_fec.concept_ontology
 WHERE proc_c.ontology = 'radlex' -- Ontology for concept classification e.g. 'snomed', 'medcin'
       AND proc_fet.type_name = 'imaging modality' -- Select only procedures with RadLex semantic type of 'imaging modality'
 GROUP BY proc_c.description, proc_c.ontology, proc_fet.type_name, proc_fet.ontology
 ORDER BY count DESC
 LIMIT 5;
```

| procedure_concept                             | concept_ontology | procedure_semantic_type | procedure_semantic_type_ontology | count |
| --------------------------------------------- | ---------------- | ----------------------- | -------------------------------- | ----- |
| magnetic resonance imaging (imaging modality) | radlex           | imaging modality        | radlex                           | 4     |
| computed tomography (imaging modality)        | radlex           | imaging modality        | radlex                           | 1     |
| projection radiography (imaging modality)     | radlex           | imaging modality        | radlex                           | 1     |