The Building Blocks of Leaf: Concepts¶
Leaf is a SQL writing and execution engine. While it is designed to be fun and intuitive for users, ultimately Leaf's most important job is to reliably and flexibly construct SQL queries using a few simple but powerful configuration rules.
Let's start with an example. We'll use an OMOP v5 database for this example, though these steps can be applied to any particular data model. We'll focus on using the person and visit_occurrence tables via views in our OMOP database.
The goals are:
-
Configure the Leaf SQL compiler.
-
Create Leaf SQL Sets for the
v_personandv_visit_occurrenceviews. -
Create basic demographics and encounter Concepts which query our clinical database.
Configuring the SQL compiler¶
Every Leaf instance assumes that there is a single, consistent field that represents patient identifiers. In OMOP databases this is the person_id field, which can be found on nearly any table with patient data. Likewise, Leaf assumes that longitudinal tables in the database will have a consistent field representing encounter identifiers. In OMOP this is the visit_occurrence_id field.
Before starting, let's create two SQL views called v_person and v_visit_occurrence to make the person and visit_occurrence tables simpler to query. This example is specific to OMOP but the approach works for other models as well. Note that pointing Leaf at views is completely optional and demonstrated here for convenience and illustrative purposes.
Our v_person view is defined as:
CREATE VIEW dbo.v_person AS
SELECT
p.person_id
, p.birth_datetime
, gender = c_gender.concept_code
, race = c_race.concept_name
, ethnicity = c_ethnicity.concept_name
, location_state = loc.state
FROM dbo.person AS p
LEFT JOIN dbo.concept AS c_gender ON p.gender_concept_id = c_gender.concept_id
LEFT JOIN dbo.concept AS c_race ON p.race_concept_id = c_race.concept_id
LEFT JOIN dbo.concept AS c_ethnicity ON p.ethnicity_concept_id = c_ethnicity.concept_id
LEFT JOIN dbo.location AS loc ON p.location_id = loc.location_id
| person_id | birth_datetime | gender | race | ethnicity | location_state |
|---|---|---|---|---|---|
| A | 1990-1-1 | F | Black or African American | Not Hispanic or Latino | NY |
| B | 1945-2-2 | M | Asian or Pacific Islander | Not Hispanic or Latino | OR |
The v_visit_occurrence view is defined as:
CREATE VIEW dbo.v_visit_occurrence AS
SELECT
o.person_id
, o.visit_occurrence_id
, o.visit_start_date
, o.visit_end_date
, o.care_site_id
, visit_type_code = c_visit.concept_code
FROM dbo.visit_occurrence AS o
LEFT JOIN dbo.concept AS c_visit ON o.visit_concept_id = c_visit.concept_id
| person_id | visit_occurrence_id | visit_start_date | visit_end_date | care_site_id | visit_type_code |
|---|---|---|---|---|---|
| A | 123 | 2011-01-01 | 2011-01-01 | site1 | OP |
| A | 456 | 2015-05-28 | 2015-06-07 | site1 | IP |
| B | 789 | 2014-09-01 | 2014-09-01 | site2 | ED |
Pretty straightforward so far. After creating the views in the database, let's start by configuring Leaf's SQL compiler, which is stored in your compiled API under /leafapi/api/appsettings.json:

Let's break it down:
-
Alias acts as a indicator to Leaf to insert an alias in a SQL statement wherever this character(s) is found (more on that in a bit). We'll set this to
@for simplicity and readability, and because it is commonly used in many Leaf configurations. -
FieldPersonId is the name of the SQL field that appears in all tables or views we'd like to query and represents unique identifiers for patients. The field
person_idcontains identifiers for patients and appears in all tables which link to a patient, so we'll choose that. -
FieldEncounterId is the name of the field that represents visit identifiers, so we'll use
visit_occurrence_id.
Great - we've now provided the most important information to help Leaf understand the basic structure of our clinical database. Note that these values are expected to be consistent and configured only once. Also, if you change them, make sure you restart the Leaf API in order for the changes to take effect.
Next, we'll move on to creating Concepts, the building blocks of Leaf queries.
Creating SQL Sets¶
We want to allow users to query Concepts using our two new views, v_person and v_visit_occurrence. To do so, let's create a Leaf SQL Set for each. Open up the Leaf client in your web browser.

-
Click
Admin->Conceptson the left sidebar.If you don't see the
Admintab, make sure you have configured your admin group correctly -
Click
Start by creating a Concept SQL Set.SQL Setsare the SQL tables, views, or subqueries that are the foundation of Concepts and provide theirFROMclauses.
-
You should see a single white box near the top. Under
SQL FROM, enterdbo.v_person. -
Next, create another
SQL Set. Click+ Create New SQL Setand fill indbo.v_visit_occurrenceunderSQL FROM. Also, check theHas Encountersbox. This indicates that Leaf should expect to find an Encounter identifier and date fields on this table. -
Under
Date Field, fill in@.visit_start_date, which you'll recall is the first date field on thev_visit_occurrenceview. Don't forget to prepend the alias placeholder@.before the field name. -
Click
Saveat the top. Now we are ready to make a few Concepts that use ourv_personandv_visit_occurrenceviews.
Creating Concepts¶
We've successfully created SQL Sets for our views, so we can now create Concepts that users can interact with.
Our next goal will be to create a basic Concept tree with the following structure:
Demographics
├── Gender
│ ├── Female
│ ├── Male
Encounters
├── Inpatient
├── Outpatient
Go to Admin -> Concepts, then click +Create New Concept.
Demographics¶

-
Under
Name, fill in "Demographics". This will be the text that users see in the Concept tree. -
Go down to
Full Textand enter "Have demographics". Users will see this text if dragged over to create a query. Why isFull Textdifferent thanName? The intent here is to make the query as descriptive as possible in something approximating an English sentence. -
Lastly, under the
SQLsection make sure theTable, View, or Subquerybox showsdbo.v_personand theWHERE Clausefield is empty. ClickSaveat the top.
As you may have noticed, this Concept is intended to simply serve as a hierachical container for Concepts under it related to Demographics. By itself it will likely not be very useful to users, and if they were to drag it over the query would be something simple like SELECT person_id FROM dbo.v_person, in other words, all patients in the table.
Next let's create the Gender Concept, which will appear under Demographics. Click +Create New Concept at the top.
Gender¶

-
Under
Name, fill in "Gender", and underFull Text, fill in "Identify with a gender". -
We want this Concept to appear beneath
Demographics, so drag the new Concept into it in order setDemographicsas the parent Concept. -
Finally, as this Concept represents data about patient gender, users presumably would expect that patients without this data should be excluded. Under
SQL->WHERE Clauseenter@.gender IS NOT NULL.
Click Save. Repeat the process for the final two demographic Concepts, Female and Male, with the following data:
- Female
- General Display
Name: "Female"Full text: "Identify as female"
- SQL
Table, View, or Subquery: "dbo.v_person"WHERE Clause: "@.gender = 'F'"
- General Display
-
Male
- General Display
Name: "Male"Full text: "Identify as male"
- SQL
Table, View, or Subquery: "dbo.v_person"WHERE Clause: "@.gender = 'M'"
In reality people don't necessarily identify with gender binaries of female or male, and clinical databases will often reflect this. Female and male here are used simply for demonstrative purposes
- General Display
Road test¶

Before proceeding to create our Encounters Concepts, let's take a moment to confirm our Demographics Concepts are working as expected by running a quick query to see how many female patients are in our database.
-
Click the
Find Patientstab in the upper-left and dragFemaleover to the first panel. -
Click
Run Query. If you see a count of patients (assuming your database has female patients), great! You can even click(i)->show detail->SQLto see the query Leaf created using the new Concept. Success!If your query didn't work (you'll get a modal window notifying you there was an error), check the Leaf logs to see if there were any syntax or other errors in the query
Encounters¶

Now that you're hopefully getting the hang of creating Concepts, let's finish by creating Encounters Concepts so users can query information by encounter types.
Remember that the structure should look like this:
Encounters
├── Inpatient
├── Outpatient
The field-level information for each Concept is (making sure to nest Inpatient and Outpatient under Encounters):
- Encounters
- General Display
Name: "Encounters"Full text: "Had an encounter"
- SQL
Table, View, or Subquery: "dbo.v_visit_occurrence"WHERE Clause:
- General Display
- Inpatient
- General Display
Name: "Inpatient"Full text: "Were admitted as an inpatient"
- SQL
Table, View, or Subquery: "dbo.v_visit_occurrence"WHERE Clause: "@.visit_type_code = 'IP'"
- General Display
- Outpatient
- General Display
Name: "Outpatient"Full text: "Had an outpatient visit"
- SQL
Table, View, or Subquery: "dbo.v_visit_occurrence"WHERE Clause: "@.visit_type_code = 'OP'"
- General Display
And that's it!
Making Concepts Searchable¶
So you've been able to make Concepts that users can query - great! Over time you'll likely find that your Concept tree will grow as you find new use cases and data sources, and may become cumbersome for users to manually traverse.

Leaf's Concept search is a powerful feature which will automatically parse and tokenize the text in your Concepts. This allows users to search for Concepts of interest to them in any word order. To enable this feature, run the app.sp_UpdateSearchIndexTables stored procedure.
Leaf search works by using a relatively simple inverted index and forward index search engine in pure SQL.
In SQL Server:
EXEC app.sp_UpdateSearchIndexTables
Warning
Be sure to run this stored procedure after any edits or additions to your Concept tree to ensure that users can search the latest text for your Concepts
Final thoughts¶
If you were able to successfully make the Concepts in this tutorial, congratulations! Hopefully this was helpful and intuitive enough for you to get started making your clinical database accessible and intuitive for your users as well.
Concepts can be extremely flexible and we've only scratched the surface of their functionality. If you'd like to learn more or have questions, head over to the Concept Reference page or jump to a section below:
- Name, Subtext, and Full Text
- Tooltips
- Patient Count
- Numeric Filters
- Adding Dropdowns
- Restricting Access
- Universal IDs
- Creating Concepts by SQL Scripts
As you were creating Concepts for your database, you may have found yourself thinking that it requires a change in perspective in how you as a developer would query a clinical database versus how best to represent Concepts to users.
We've found that this is just as much an art as a science, and it's ultimately an iterative process, so don't worry about getting it perfect the first time.