Skip to content

Query API

Maarten Vroegindeweij edited this page Feb 18, 2026 · 1 revision

Query API

Ifc-Factory provides a fluent, chainable query builder and composable filter predicates for finding entities in an IfcModel.

QueryBuilder

Basic Usage

import { QueryBuilder } from '@ifc-factory/core';

// Get all walls
const walls = new QueryBuilder(model)
  .ofType('IfcWall')
  .execute();

// Get first door
const door = new QueryBuilder(model)
  .ofType('IfcDoor')
  .first();

// Count windows
const windowCount = new QueryBuilder(model)
  .ofType('IfcWindow')
  .count();

Chaining Methods

All filter methods return the QueryBuilder, so they can be chained:

const results = new QueryBuilder(model)
  .ofType('IfcWall')                                    // Filter by entity type
  .whereProperty('Name', 'Exterior Wall')               // Filter by property value
  .where(e => e.PredefinedType === 'SOLIDWALL')        // Custom predicate
  .limit(10)                                            // Max results
  .execute();                                           // Run query

Methods

.ofType(typeName: string): QueryBuilder

Filter entities by their IFC type name.

.ofType('IfcWall')
.ofType('IfcDoor')
.ofType('IfcRelAggregates')

.where(predicate: (entity: IfcGenericEntity) => boolean): QueryBuilder

Filter with a custom predicate function. Can be called multiple times — all predicates must match (AND).

// Custom logic
.where(e => e.Name !== null && e.Name !== '')

// Numeric comparison
.where(e => typeof e.OverallHeight === 'number' && e.OverallHeight > 2.0)

// Check for existence
.where(e => 'PredefinedType' in e && e.PredefinedType !== null)

.whereProperty(name: string, value: unknown): QueryBuilder

Shorthand for filtering by a property's exact value.

.whereProperty('Name', 'Exterior Wall')
.whereProperty('PredefinedType', 'SOLIDWALL')
.whereProperty('IsExternal', true)

Equivalent to .where(e => e[name] === value).

.limit(max: number): QueryBuilder

Limit the number of results returned.

.limit(10)   // Return at most 10 results
.limit(1)    // Return at most 1 result

.execute(): IfcGenericEntity[]

Run the query and return all matching entities as an array.

const results = new QueryBuilder(model)
  .ofType('IfcWall')
  .execute();
// Returns: IfcGenericEntity[]

.first(): IfcGenericEntity | undefined

Run the query and return the first matching entity, or undefined if none match.

const project = new QueryBuilder(model)
  .ofType('IfcProject')
  .first();
// Returns: IfcGenericEntity | undefined

.count(): number

Run the query and return the count of matching entities.

const wallCount = new QueryBuilder(model)
  .ofType('IfcWall')
  .count();
// Returns: number

Composable Filters

For more complex queries, use composable filter predicates with boolean combinators.

import { byType, byProperty, byPropertyExists, and, or, not } from '@ifc-factory/core';

Filter Predicates

byType(typeName: string)

Match entities by type.

const isWall = byType('IfcWall');
// Matches entities where entity.type === 'IfcWall'

byProperty(name: string, value: unknown)

Match entities by property value.

const isExternal = byProperty('IsExternal', true);
const namedExterior = byProperty('Name', 'Exterior Wall');

byPropertyExists(name: string)

Match entities that have a non-null, non-undefined value for the given property.

const hasName = byPropertyExists('Name');
const hasDescription = byPropertyExists('Description');

Boolean Combinators

and(...predicates)

All predicates must match.

const externalWall = and(
  byType('IfcWall'),
  byProperty('PredefinedType', 'SOLIDWALL')
);

or(...predicates)

At least one predicate must match.

const wallOrDoor = or(
  byType('IfcWall'),
  byType('IfcDoor')
);

not(predicate)

Invert a predicate.

const notExternal = not(byProperty('IsExternal', true));

Combining Everything

import { QueryBuilder, byType, byProperty, byPropertyExists, and, or, not } from '@ifc-factory/core';

// Complex query: exterior walls or doors, with a name, not using USERDEFINED type
const filter = and(
  or(
    byType('IfcWall'),
    byType('IfcDoor')
  ),
  byPropertyExists('Name'),
  not(byProperty('PredefinedType', 'USERDEFINED'))
);

const results = new QueryBuilder(model)
  .where(filter)
  .execute();

console.log(`Found ${results.length} matching elements`);
for (const entity of results) {
  console.log(`  ${entity.type}: ${entity.Name} [#${entity.expressID}]`);
}

Using Filters Without QueryBuilder

Filters are plain predicate functions (entity: IfcGenericEntity) => boolean, so you can use them anywhere:

const isExternalWall = and(byType('IfcWall'), byProperty('IsExternal', true));

// Use with Array.filter
const allEntities = model.getAllOfType('IfcWall');
const externalWalls = allEntities.filter(isExternalWall);

// Use in conditions
const entity = model.get(42);
if (entity && isExternalWall(entity)) {
  console.log('Entity #42 is an external wall');
}

Examples

Find all load-bearing walls

const loadBearingWalls = new QueryBuilder(model)
  .ofType('IfcWall')
  .where(e => {
    // Check property sets for LoadBearing
    const psets = getPropertySets(model, e.expressID);
    for (const pset of psets) {
      const props = pset.HasProperties as number[];
      for (const propId of props) {
        const prop = model.get(propId);
        if (prop?.Name === 'LoadBearing' && prop?.NominalValue === true) {
          return true;
        }
      }
    }
    return false;
  })
  .execute();

Find all elements on a specific storey

const groundFloorElements = model.getContainedElements(groundFloorId);

// Further filter to only walls
const groundFloorWalls = groundFloorElements.filter(e => e.type === 'IfcWall');

Count entities by type

const typeCounts = new Map<string, number>();
const entityTypes = ['IfcWall', 'IfcDoor', 'IfcWindow', 'IfcSlab', 'IfcColumn', 'IfcBeam'];

for (const type of entityTypes) {
  const count = new QueryBuilder(model).ofType(type).count();
  if (count > 0) {
    typeCounts.set(type, count);
  }
}

for (const [type, count] of typeCounts) {
  console.log(`${type}: ${count}`);
}

See Also

Clone this wiki locally