Two-Step Filtering
Many DAQS rules filter in two steps — first FamilySymbols, then FamilyInstances.
This is not a workaround. It is a direct result of how Revit stores data.
Why it exists
In Revit, some data lives on the Type (FamilySymbol) and other data lives on the Instance (FamilyInstance).
- Category → on the FamilySymbol
- Assembly Code → on the FamilySymbol
- Mark → on the FamilyInstance
- Level → on the FamilyInstance
When you want to validate an instance based on its type's data, you need both objects.
For example:
All FamilyInstances in category Doors must have a Mark.
To write this rule, you need:
- Category → comes from FamilySymbol.values.category
- Mark → comes from FamilyInstance.values.mark
You cannot get both from a single object. The instance does not carry category. The symbol does not carry mark.
The pattern
Step 1 — find the relevant symbol IDs:
$[type = "FamilySymbol" and values.category.name = "Doors"].id
This returns a list of IDs of all door type symbols.
Step 2 — find instances whose parent is one of those symbols:
$[type = "FamilyInstance" and parent.id in $doorSymbolIds]
Combined into a single filter:
(
$doorSymbolIds := $[type = "FamilySymbol" and values.category.name = "Doors"].id;
$[type = "FamilyInstance" and parent.id in $doorSymbolIds].{
"id": id,
"type": type,
"name": name,
"mark": values.mark
}
)
The ; separates the variable binding from the main query.
The last expression is what the filter returns.
When to use two-step filtering
Use two steps when your scope condition involves type-level data but you are validating instance-level data — or vice versa.
Common cases:
| Scope condition | Validated value | Steps needed |
|---|---|---|
| Category = Doors | Instance Mark | Two — category on symbol, mark on instance |
Assembly Code starts with 32. |
Instance Mark | Two — assembly code on symbol, mark on instance |
| isEditable = true | Type Mark | Two — isEditable on family, type mark on symbol |
| Instance on Level X | Instance parameter | One — everything on instance |
What the ; separator does
Inside a ( ) block, ; separates statements.
Variables assigned with := are available to all following statements.
(
$symbols := $[type = "FamilySymbol" and values.category.name = "Doors"];
$ids := $symbols.id;
$[type = "FamilyInstance" and parent.id in $ids].{
"id": id,
"name": name,
"mark": values.mark
}
)
The last expression is always the return value of the block.
Summary
Two-step filtering is necessary because:
- Type data lives on
FamilySymbol - Instance data lives on
FamilyInstance - These are separate objects in the flat JSON array
When your rule's scope condition and its validated value are on different object types, you need two steps.