Guide to querying IFC data with IFClite.
IFClite provides multiple query interfaces:
flowchart TB
subgraph Sources["Data Sources"]
Entities["Entity Table"]
Props["Property Table"]
Graph["Relationship Graph"]
end
subgraph Query["Query Interfaces"]
Fluent["Fluent API"]
SQL["SQL (DuckDB)"]
Direct["Direct Access"]
end
subgraph Output["Output"]
Arrays["Entity Arrays"]
Values["Property Values"]
Trees["Hierarchy Trees"]
end
Sources --> Query
Query --> Output
import { IfcQuery } from '@ifc-lite/query';
const query = new IfcQuery(parseResult);
// Get all walls
const walls = query.walls().toArray();
// Get all doors
const doors = query.doors().toArray();
// Get all windows
const windows = query.windows().toArray();
// Get specific entity types
const beams = query.ofType('IFCBEAM').toArray();
const columns = query.ofType('IFCCOLUMN').toArray();
| Method | Entity Type |
|---|---|
.walls() |
IFCWALL, IFCWALLSTANDARDCASE |
.doors() |
IFCDOOR |
.windows() |
IFCWINDOW |
.slabs() |
IFCSLAB |
.roofs() |
IFCROOF |
.stairs() |
IFCSTAIR, IFCSTAIRFLIGHT |
.columns() |
IFCCOLUMN |
.beams() |
IFCBEAM |
.spaces() |
IFCSPACE |
.furniture() |
IFCFURNISHINGELEMENT |
// Filter by property value
const externalWalls = query
.walls()
.whereProperty('Pset_WallCommon', 'IsExternal', '=', true)
.toArray();
// Filter by numeric comparison
const fireRatedWalls = query
.walls()
.whereProperty('Pset_WallCommon', 'FireRating', '>=', 60)
.toArray();
// Filter by string pattern
const loadBearing = query
.walls()
.whereProperty('Pset_WallCommon', 'LoadBearing', '=', true)
.toArray();
flowchart LR
Start["query"]
Type["walls()"]
Filter1["whereProperty()"]
Filter2["whereQuantity()"]
Select["select()"]
Output["toArray()"]
Start --> Type --> Filter1 --> Filter2 --> Select --> Output
// Complex query chain
const results = query
.walls()
.whereProperty('Pset_WallCommon', 'IsExternal', '=', true)
.whereProperty('Pset_WallCommon', 'FireRating', '>=', 60)
.whereQuantity('NetArea', '>', 10)
.select(['expressId', 'name', 'type'])
.toArray();
console.log(results);
// [
// { expressId: 123, name: 'Wall-001', type: 'IFCWALL' },
// { expressId: 456, name: 'Wall-002', type: 'IFCWALLSTANDARDCASE' },
// ...
// ]
graph TD
Project["IfcProject"]
Site["IfcSite"]
Building["IfcBuilding"]
Storey1["Storey: Ground Floor"]
Storey2["Storey: First Floor"]
Wall1["Wall-001"]
Wall2["Wall-002"]
Door["Door-001"]
Project --> Site
Site --> Building
Building --> Storey1
Building --> Storey2
Storey1 --> Wall1
Storey1 --> Wall2
Wall1 --> Door
// Get all storeys
const storeys = query.storeys().toArray();
// Get elements on a specific storey
const groundFloor = query
.storey('Ground Floor')
.contains()
.toArray();
// Get all elements in a building
const buildingElements = query
.building('Main Building')
.allContained() // Recursive
.toArray();
// Navigate up the hierarchy
const wall = query.entity(123);
const storey = wall.containedIn().first();
const building = storey.containedIn().first();
// Get entities related by spatial structure
const contained = query
.entity(storeyId)
.contains()
.toArray();
// Get container
const container = query
.entity(wallId)
.containedIn()
.first();
// Get adjacent spaces
const adjacentSpaces = query
.entity(spaceId)
.adjacent()
.toArray();
// Get materials for an entity
const materials = query
.entity(wallId)
.materials()
.toArray();
// Get property sets
const psets = query
.entity(wallId)
.propertySets()
.toArray();
// Get openings in a wall
const openings = query
.entity(wallId)
.related('IfcRelVoidsElement')
.toArray();
// Get filling elements (doors/windows in openings)
const fillings = query
.entity(openingId)
.related('IfcRelFillsElement')
.toArray();
| Relationship | Description |
|---|---|
IfcRelContainedInSpatialStructure |
Element → Spatial container |
IfcRelAggregates |
Parent → Children (decomposition) |
IfcRelVoidsElement |
Element → Opening |
IfcRelFillsElement |
Opening → Filling (door/window) |
IfcRelAssociatesMaterial |
Element → Material |
IfcRelDefinesByProperties |
Element → Property sets |
IfcRelDefinesByType |
Element → Type definition |
For complex analytics, use SQL via DuckDB:
import { IfcQuery } from '@ifc-lite/query';
const query = new IfcQuery(parseResult);
// Enable SQL mode (loads DuckDB-WASM)
await query.enableSQL();
// Run SQL queries
const result = await query.sql(`
SELECT
type,
COUNT(*) as count,
AVG(CAST(props->>'Pset_WallCommon.FireRating' AS INTEGER)) as avg_fire_rating
FROM entities
WHERE type LIKE 'IFCWALL%'
GROUP BY type
ORDER BY count DESC
`);
console.table(result);
// ┌──────────────────────┬───────┬─────────────────┐
// │ type │ count │ avg_fire_rating │
// ├──────────────────────┼───────┼─────────────────┤
// │ IFCWALLSTANDARDCASE │ 42 │ 45 │
// │ IFCWALL │ 18 │ 30 │
// └──────────────────────┴───────┴─────────────────┘
// Entities table
interface EntitiesTable {
express_id: number;
type: string;
global_id: string;
name: string | null;
description: string | null;
has_geometry: boolean;
props: JSON; // All properties as JSON
}
// Properties table
interface PropertiesTable {
entity_id: number;
pset_name: string;
prop_name: string;
value: string;
value_type: string;
}
// Quantities table
interface QuantitiesTable {
entity_id: number;
name: string;
value: number;
unit: string;
}
// Relationships table
interface RelationshipsTable {
from_id: number;
to_id: number;
rel_type: string;
}
-- Find walls with their storey names
SELECT
e.express_id,
e.name as wall_name,
s.name as storey_name
FROM entities e
JOIN relationships r ON e.express_id = r.from_id
JOIN entities s ON r.to_id = s.express_id
WHERE e.type LIKE 'IFCWALL%'
AND r.rel_type = 'IfcRelContainedInSpatialStructure'
AND s.type = 'IFCBUILDINGSTOREY';
-- Calculate total area by entity type
SELECT
e.type,
SUM(q.value) as total_area
FROM entities e
JOIN quantities q ON e.express_id = q.entity_id
WHERE q.name = 'NetArea'
GROUP BY e.type
ORDER BY total_area DESC;
-- Find entities with missing fire ratings
SELECT express_id, name, type
FROM entities
WHERE type LIKE 'IFCWALL%'
AND props->>'Pset_WallCommon.FireRating' IS NULL;
For performance-critical operations, access columnar data directly:
const data = parseResult.data;
// Access entity table
const entityTable = data.entities;
console.log(`Total entities: ${entityTable.count}`);
// Iterate efficiently
for (let i = 0; i < entityTable.count; i++) {
const expressId = entityTable.expressIds[i];
const typeEnum = entityTable.typeEnums[i];
const nameIdx = entityTable.nameIndices[i];
const name = data.strings.get(nameIdx);
}
// Access property table
const propTable = data.properties;
for (let i = 0; i < propTable.count; i++) {
const entityId = propTable.entityIds[i];
const value = propTable.values[i];
}
graph LR
subgraph Performance["Query Performance"]
Fluent["Fluent API<br/>~1ms"]
Direct["Direct Access<br/>~0.1ms"]
SQL["SQL Query<br/>~10-100ms"]
end
Simple["Simple Queries"] --> Fluent
Bulk["Bulk Operations"] --> Direct
Complex["Complex Analytics"] --> SQL
// Efficient: filter by type first
const result = query
.walls()
.whereProperty('Pset_WallCommon', 'IsExternal', '=', true)
.toArray();
// Inefficient: filter all entities
const result = query
.all()
.whereProperty('Pset_WallCommon', 'IsExternal', '=', true)
.ofType('IFCWALL')
.toArray();