BQL 101

BQL (Building Query Language) is the core of Retriever. BQL is to buildings as SQL is to other databases. BQL is fast and flexible enough to express a wide variety of conditions for QA/QC, data extraction or other purposes.

This post runs through a few aspects of BQL and introduces what features are available inside of Retriever to work with Revit models.

1. Predicate = Relationship

Predicates are the foundation of BQL. They look like

category(Element, CategoryName)

Predicates define relationships. They are not functions that define an action as in Python or C#. Relationships are different because they go both forward and backward -- you can use category to find one given element's category name, find all elements of a given category, or even list all elements and their categories.

The category predicate above defines the following relationship: the parameter Element is of category of the parameter CategoryName in Revit.

Predicates start with a lowercase letter, like category. Parameters start with an uppercase letter, like Element and CategoryName.

Parameters in a predicate can be variables or constants. For example,

% Elements that are windows
category(Element, 'Windows')

% Element with id @243 is a window
category(@243, 'Windows') 

Below are some examples with the parameter predicate:

% Elements whose ‘Fire Rating’ parameter is one hour
parameter(ElementID, ‘Fire Rating’, ‘1 hour’)

% The fire rating of element with id 330542
parameter(@330542, ‘Fire Rating’, FireRating)

2. Built-in predicates

Retriever ships with several predicates like category and parameter built in. All of these predicates are available in every script. You can learn more about what's inside of Retriever by browsing the examples, or from these blog posts:

BQL Predicates 1 : category, parameter and level


3. Define your own predicate

To define a new predicate, there are two parts: the head and body. 

Let's define a new predicate we call windowOnLevel, which finds all windows on a level.

% Head :- Body
windowOnLevel(Window, Level) :- 
    category(Window, 'Windows'),
    level(Window, Level).

:- is used to separate the head from the body. Predicates in the body are separated by ",": in BQL "," means "and". The header predicate is true only if all of the body predicates hold.

4. Ask a question?

You can ask a question by adding a question mark at the end of a predicate, either one you have defined or one that is built-in.

parameter(ElementID, ‘Fire Rating’, FireRating)?
category(Element, 'Windows')?
windowOnLevel(Window, Level)?
windowOnLevel(Window, 'Floor 1')? 

You can ask one question per BQL rule. 

5. select and test

If you've played around with our example rules, you will notice most of them have predicates select or test. The names have special meanings for Retriever.

select is a short way to define the final predicate to run. In other word, you don't need to add a question mark, and Retriever engine will automatically run select.

% Find all families and their names.
select(Family, Name) :- 
    class(Family, 'Family'), 
    property(Family, 'Name', Name).

The test predicate takes the same parameters as select, and adds more predicates to define what it means for the results from select pass. Results from select that don't satisfy test are reported by Retriever as rule failures.

select(Family, Name) :-
    class(Family, 'Family'),
    property(Family, 'Name', Name).

% Test if family names match Title Case.
test(Family, Name) :-
    select(Family, Name),
    matchRegex(Name, '^[A-Z][a-z0-9]*( [A-Z][a-z0-9]+)*$').
Ye Wang