Contents Menu Expand Light mode Dark mode Auto light/dark mode
IFC as a Database
IFC as a Database

Use Cases

  • Use Cases

Schema Conversion

  • Converting IFC schema to EdgeDB schema (ESDL)
  • Migrating the entire IFC schema into EdgeDB
  • Necessary modifications to the IFC schema

Inserting Elements

  • IFC Insert Strategies

Querying Elements

  • Query Building
  • Query by element name(s)/GlobalId(s)
  • Nested Object links
  • Slicing in the Spatial Hierarchy
  • Large queries

Updating Elements

  • Updating EdgeDB with local IFC changes
  • EdgeDB update statement issue
  • The issue of property updates and “overlinking”

Validation

  • Validation Strategy
  • Round-tripping cube-advanced-brep.ifc

CI/CD

  • Unittesting EdgeDB schemas
Back to top

Nested Object links#

A manual investigation of the complex nested object property links found in the IFC schema. This manual query building exercise functions as a stepping stone towards building an automatic query generator for IFC in EdgeDB.

Note

The intention is that the query builder will in fact work on any EdgeDB schema, not only for IFC.

The rooted IFC element IfcBeam is used as a source for further investigation. See the IfcBeam docs for more information about the IfcBeam.

The ifc file MyBeam.ifc is first uploaded to an EdgeDB instance. See below for the full code employed for both uploading IFC and subsequent queries.

import json

from ifcdb import EdgeIO
from ifcdb.utils import top_dir


def main(ifc_file, refresh_db=False):
    db_name = ifc_file.replace(".ifc", "").replace("-", "_")
    ifc_path = top_dir() / "files" / ifc_file

    with EdgeIO(db_schema_dir=f"temp/{db_name}/dbschema", ifc_schema="IFC4x1", database_name=db_name) as io:
        if refresh_db:
            io.create_schema_from_ifc_file(ifc_path=ifc_path)
            io.setup_database(delete_existing_migrations=True)
            io.insert_ifc(ifc_path)

        # Queries
        for qref in ["q1", "q2", "q3"]:
            q1_res = json.loads(io.client.query_json(open(f"{qref}.esdl", "r").read()))
            with open(f"{qref}_output.json", "w", encoding="utf-8") as f:
                json.dump(q1_res, f, indent=4)


if __name__ == "__main__":
    main("MyBeam.ifc", refresh_db=False)

Query 1 -> Basic Select#

The basic select statement to get only the top level of the IfcBeam class can be written like this:

SELECT IfcBeam {
    # Properties pointing to native types (str, float64, int64, etc.)
    GlobalId,
    Description,
    Name,
    ObjectType,
    Tag,
    PredefinedType,
    # Links to other objects
    OwnerHistory,
    ObjectPlacement,
    Representation,
    # These are specific EdgeDB attributes to get more information about the object itself
    _e_type := .__type__.name
}

which generates the following output:

[
    {
        "GlobalId": "3PXsnq_3qHxBd2w2f4ZOUQ",
        "Description": "IPE400",
        "Name": "MyBeam",
        "ObjectType": "Beam",
        "Tag": "MyBeam",
        "PredefinedType": null,
        "OwnerHistory": {
            "id": "ebe57210-fced-11ec-97f5-57e3b32f3dcd"
        },
        "ObjectPlacement": {
            "id": "eee839b6-fced-11ec-97f5-73fd14217afc"
        },
        "Representation": {
            "id": "ee4d8ae2-fced-11ec-97f5-7f55f62e5c6b"
        },
        "_e_type": "default::IfcBeam"
    }
]

Query 2 -> Get properties of linked objects#

SELECT IfcBeam {
    # Properties pointing to native types (str, float64, int64, etc.)
    GlobalId,
    Description,
    Name,
    ObjectType,
    Tag,
    PredefinedType,
    # Links to other objects
    OwnerHistory : {
        # Properties
        LastModifiedDate,
        CreationDate,
        # Links to other objects
        OwningUser,
        OwningApplication,
        State,
        ChangeAction,
        LastModifyingUser,
        LastModifyingApplication,
        # These are specific EdgeDB attributes to get more information about the object itself
        _e_type := .__type__.name
    },
    ObjectPlacement : {
        # ObjectPlacement points to an abstract superclass "IfcObjectPlacement".
        # Therefore we need to consider various subtypes using the [is ..].key
        # (to reduce size of this file and since we know that for this scenario ObjectPlacement is pointing to an
        # IfcLocalPlacement, we will skip the other subtypes "IfcLinearPlacement" & "IfcGridPlacement"
        [is IfcLocalPlacement].PlacementRelTo,
        [is IfcLocalPlacement].RelativePlacement,
        # These are specific EdgeDB attributes to get more information about the object itself
        _e_type := .__type__.name
    },
    Representation : {
        # Points to an abstract superclass "IfcProductRepresentation". However only the subtype
        # "IfcMaterialDefinitionRepresentation" has any unique properties not associated with its parent class and
        # it is not used in this particular test IFC file and is therefore omitted.
        # Properties
        Name,
        Description,
        # Links to other objects
        Representations,
        # These are specific EdgeDB attributes to get more information about the object itself
        _e_type := .__type__.name
    }
}

which returns the following output

[
    {
        "GlobalId": "3PXsnq_3qHxBd2w2f4ZOUQ",
        "Description": "IPE400",
        "Name": "MyBeam",
        "ObjectType": "Beam",
        "Tag": "MyBeam",
        "PredefinedType": null,
        "OwnerHistory": {
            "LastModifiedDate": 1656570506,
            "CreationDate": 1656570506,
            "OwningUser": {
                "id": "e9fde1da-fced-11ec-97f5-6f9f1b461637"
            },
            "OwningApplication": {
                "id": "ea228a44-fced-11ec-97f5-d744ce069446"
            },
            "State": "READWRITE",
            "ChangeAction": null,
            "LastModifyingUser": {
                "id": "e9fde1da-fced-11ec-97f5-6f9f1b461637"
            },
            "LastModifyingApplication": {
                "id": "ea228a44-fced-11ec-97f5-d744ce069446"
            },
            "_e_type": "default::IfcOwnerHistory"
        },
        "ObjectPlacement": {
            "PlacementRelTo": {
                "id": "ee386f0e-fced-11ec-97f5-b3f35fe72359"
            },
            "RelativePlacement": {
                "id": "eee83704-fced-11ec-97f5-dfb5f1ae7674"
            },
            "_e_type": "default::IfcLocalPlacement"
        },
        "Representation": {
            "Name": null,
            "Description": null,
            "Representations": [
                {
                    "id": "ed8f000e-fced-11ec-97f5-57758b6c7591"
                },
                {
                    "id": "eda27b02-fced-11ec-97f5-13a248609d5b"
                }
            ],
            "_e_type": "default::IfcProductDefinitionShape"
        }
    }
]

In the outputted results you can notice that there is a lot of elements with "id": "..". This is EdgeDB’s way of letting us know that this property is linked to another object.

At the time of writing, there is no “automatic” way of asking in a single query for all related object’s and their properties without knowing upfront what exactly are the related object type’s and the names of their properties.

As a consequence of this, it is necessary to explicitly define the entire path of every related object and their properties. This will be the final step in this experiment

Query 3 -> The completed query shape#

As a final piece in this exercise, it will be attempted to write the entire query shape to return all related object properties originating from the IfcBeam class. In other terms, the number of occurrences of "id": ".." in the result should be kept as low as possible.

SELECT IfcBeam {
    # Properties pointing to native types (str, float64, int64, etc.)
    GlobalId,
    Description,
    Name,
    ObjectType,
    Tag,
    PredefinedType,
    # Links to other objects
    OwnerHistory : { # IfcOwnerHistory
        # Properties
        LastModifiedDate,
        CreationDate,
        # Links to other objects
        OwningUser : { # IfcPersonAndOrganization
            ThePerson : { # IfcPerson
                Identification,
                FamilyName,
                GivenName,
                MiddleNames,
                PrefixTitles,
                SuffixTitles,
                Roles : { # IfcActorRole
                    Role,
                    UserDefinedRole,
                    Description,
                    _e_type := .__type__.name
                },
                Addresses : { # IfcAddress
                    Purpose,
                    Description,
                    UserDefinedPurpose,
                    _e_type := .__type__.name
                },
                _e_type := .__type__.name
            },
            TheOrganization : { # IfcOrganization
                Identification,
                Name,
                Description,
                Roles : { # IfcActorRole
                    Role,
                    UserDefinedRole,
                    Description,
                    _e_type := .__type__.name
                },
                Addresses : { # IfcAddress
                    Purpose,
                    Description,
                    UserDefinedPurpose,
                    _e_type := .__type__.name
                },
                _e_type := .__type__.name
            },
            Roles : { # IfcActorRole
                Role,
                UserDefinedRole,
                Description,
                _e_type := .__type__.name
            }
        },
        OwningApplication : { # IfcApplication
            ApplicationDeveloper : { # IfcOrganization
                Identification,
                Name,
                Description,
                Roles : { # IfcActorRole
                    Role,
                    UserDefinedRole,
                    Description,
                    _e_type := .__type__.name
                },
                Addresses : { # IfcAddress
                    Purpose,
                    Description,
                    UserDefinedPurpose
                },
                _e_type := .__type__.name
            },
            Version,
            ApplicationFullName,
            ApplicationIdentifier,
            _e_type := .__type__.name
        },
        State,
        ChangeAction,
        LastModifyingUser : { # IfcPersonAndOrganization
            ThePerson : { # IfcPerson
                Identification,
                FamilyName,
                GivenName,
                MiddleNames,
                PrefixTitles,
                SuffixTitles,
                Roles : { # IfcActorRole
                    Role,
                    UserDefinedRole,
                    Description,
                    _e_type := .__type__.name
                },
                Addresses : { # IfcAddress
                    Purpose,
                    Description,
                    UserDefinedPurpose,
                    _e_type := .__type__.name
                },
                _e_type := .__type__.name
            },
            TheOrganization : { # IfcOrganization
                Identification,
                Name,
                Description,
                Roles : { # IfcActorRole
                    Role,
                    UserDefinedRole,
                    Description,
                    _e_type := .__type__.name
                },
                Addresses : { # IfcAddress
                    Purpose,
                    Description,
                    UserDefinedPurpose,
                    _e_type := .__type__.name
                },
                _e_type := .__type__.name
            },
            Roles : { # IfcActorRole
                Role,
                UserDefinedRole,
                Description,
                _e_type := .__type__.name
            },
            _e_type := .__type__.name
        },
        LastModifyingApplication : { # IfcApplication
            ApplicationDeveloper : { # IfcOrganization
                Identification,
                Name,
                Description,
                Roles : { # IfcActorRole
                    Role,
                    UserDefinedRole,
                    Description,
                    _e_type := .__type__.name
                },
                Addresses : { # IfcAddress
                    Purpose,
                    Description,
                    UserDefinedPurpose,
                    _e_type := .__type__.name
                },
                _e_type := .__type__.name
            },
            Version,
            ApplicationFullName,
            ApplicationIdentifier,
            _e_type := .__type__.name
        },
        # These are specific EdgeDB attributes to get more information about the object itself
        _e_type := .__type__.name
    },
    ObjectPlacement : { # IfcObjectPlacement
        # ObjectPlacement points to an abstract superclass "IfcObjectPlacement".
        # Therefore we need to consider various subtypes using the [is ..].key
        # (to reduce size of this file and since we know that for this scenario ObjectPlacement is pointing to an
        # IfcLocalPlacement, we will skip the other subtypes "IfcLinearPlacement" & "IfcGridPlacement"
        [is IfcLocalPlacement].PlacementRelTo : {
            id,
            _e_type := .__type__.name
        },
        # PlacementRelTo links to same type of object "IfcObjectPlacement".
        # Therefore it is considered not necessary to follow the path further, given that this information would have
        # to be collected in a separate step
        [is IfcLocalPlacement].RelativePlacement : { # IfcAxis2Placement
            # IfcAxis2Placement is a Select object. Which means it can be one of many objects.
            # It is however, not known how
            [is IfcAxis2Placement3D].Location : { # IfcPoint
                [is IfcCartesianPoint].Coordinates
            },
            [is IfcAxis2Placement3D].RefDirection: { # IfcDirection
                DirectionRatios
            },
            [is IfcAxis2Placement3D].Axis,
            # These are specific EdgeDB attributes to get more information about the object itself
            _e_type := .__type__.name
        },
        # These are specific EdgeDB attributes to get more information about the object itself
        _e_type := .__type__.name
    },
    Representation : { # IfcProductRepresentation
        # Points to an abstract superclass "IfcProductRepresentation". However only the subtype
        # "IfcMaterialDefinitionRepresentation" has any unique properties not associated with its parent class and
        # it is not used in this particular test IFC file and is therefore omitted.
        # Properties
        Name,
        Description,
        # Links to other objects
        Representations : { # IfcRepresentation
            [is IfcShapeRepresentation].RepresentationIdentifier,
            [is IfcShapeRepresentation].RepresentationType,
            [is IfcShapeRepresentation].ContextOfItems : { # IfcRepresentationContext
                ContextIdentifier,
                ContextType,
                [is IfcGeometricRepresentationContext].CoordinateSpaceDimension,
                [is IfcGeometricRepresentationContext].Precision,
                [is IfcGeometricRepresentationContext].WorldCoordinateSystem : { # IfcAxis2Placement
                    # IfcAxis2Placement is a Select object. Which means it can be one of many objects.
                    # It is however, not known how
                    [is IfcAxis2Placement3D].Location : { # IfcPoint
                        [is IfcCartesianPoint].Coordinates
                    },
                    [is IfcAxis2Placement3D].RefDirection: { # IfcDirection
                        DirectionRatios
                    },
                    [is IfcAxis2Placement3D].Axis,
                    # These are specific EdgeDB attributes to get more information about the object itself
                    _e_type := .__type__.name
                },
                [is IfcGeometricRepresentationContext].TrueNorth : { # IfcDirection
                    DirectionRatios
                },
                _e_type := .__type__.name
            },
            [is IfcShapeRepresentation].Items : { # IfcRepresentationItem
                # IfcRepresentationItem is an abstract class that represents > 100 different types of representation
                # types. Therefore only the 2 subtypes found in the IFC file is included
                [is IfcExtrudedAreaSolid].SweptArea : { # IfcProfileDef
                    # IfcProfileDef is an abstract superclass. Here only the IfcIShapeProfileDef is included.
                    [is IfcIShapeProfileDef].ProfileType,
                    [is IfcIShapeProfileDef].ProfileName,
                    [is IfcIShapeProfileDef].Position : { # IfcAxis2Placement2D
                        Location : { # IfcPoint
                            [is IfcCartesianPoint].Coordinates
                        },
                        RefDirection : { # IfcDirection
                            DirectionRatios
                        },
                    },
                    [is IfcIShapeProfileDef].OverallWidth,
                    [is IfcIShapeProfileDef].OverallDepth,
                    [is IfcIShapeProfileDef].WebThickness,
                    [is IfcIShapeProfileDef].FlangeThickness,
                    [is IfcIShapeProfileDef].FilletRadius,
                    [is IfcIShapeProfileDef].FlangeEdgeRadius,
                    [is IfcIShapeProfileDef].FlangeSlope
                },
                [is IfcExtrudedAreaSolid].Position : { # IfcAxis2Placement3D
                    Location : { # IfcPoint
                        [is IfcCartesianPoint].Coordinates
                    },
                    Axis : { # IfcDirection
                        DirectionRatios
                    },
                    RefDirection : { # IfcDirection
                        DirectionRatios
                    },
                },
                [is IfcExtrudedAreaSolid].ExtrudedDirection : { # IfcDirection
                    DirectionRatios
                },
                [is IfcExtrudedAreaSolid].Depth,
                [is IfcPolyline].Points : { # IfcCartesianPoint
                    Coordinates
                }
            }
        },
        # These are specific EdgeDB attributes to get more information about the object itself
        _e_type := .__type__.name
    }
}

which returns the following output

[
    {
        "GlobalId": "3PXsnq_3qHxBd2w2f4ZOUQ",
        "Description": "IPE400",
        "Name": "MyBeam",
        "ObjectType": "Beam",
        "Tag": "MyBeam",
        "PredefinedType": null,
        "OwnerHistory": {
            "LastModifiedDate": 1656570506,
            "CreationDate": 1656570506,
            "OwningUser": {
                "ThePerson": {
                    "Identification": "AdaUser",
                    "FamilyName": null,
                    "GivenName": null,
                    "MiddleNames": null,
                    "PrefixTitles": null,
                    "SuffixTitles": null,
                    "Roles": [],
                    "Addresses": [],
                    "_e_type": "default::IfcPerson"
                },
                "TheOrganization": {
                    "Identification": "ADA",
                    "Name": "Assembly For Design and Analysis",
                    "Description": null,
                    "Roles": [],
                    "Addresses": [],
                    "_e_type": "default::IfcOrganization"
                },
                "Roles": []
            },
            "OwningApplication": {
                "ApplicationDeveloper": {
                    "Identification": "ADA",
                    "Name": "Assembly For Design and Analysis",
                    "Description": null,
                    "Roles": [],
                    "Addresses": [],
                    "_e_type": "default::IfcOrganization"
                },
                "Version": "XXX",
                "ApplicationFullName": "ADA",
                "ApplicationIdentifier": "ADA",
                "_e_type": "default::IfcApplication"
            },
            "State": "READWRITE",
            "ChangeAction": null,
            "LastModifyingUser": {
                "ThePerson": {
                    "Identification": "AdaUser",
                    "FamilyName": null,
                    "GivenName": null,
                    "MiddleNames": null,
                    "PrefixTitles": null,
                    "SuffixTitles": null,
                    "Roles": [],
                    "Addresses": [],
                    "_e_type": "default::IfcPerson"
                },
                "TheOrganization": {
                    "Identification": "ADA",
                    "Name": "Assembly For Design and Analysis",
                    "Description": null,
                    "Roles": [],
                    "Addresses": [],
                    "_e_type": "default::IfcOrganization"
                },
                "Roles": [],
                "_e_type": "default::IfcPersonAndOrganization"
            },
            "LastModifyingApplication": {
                "ApplicationDeveloper": {
                    "Identification": "ADA",
                    "Name": "Assembly For Design and Analysis",
                    "Description": null,
                    "Roles": [],
                    "Addresses": [],
                    "_e_type": "default::IfcOrganization"
                },
                "Version": "XXX",
                "ApplicationFullName": "ADA",
                "ApplicationIdentifier": "ADA",
                "_e_type": "default::IfcApplication"
            },
            "_e_type": "default::IfcOwnerHistory"
        },
        "ObjectPlacement": {
            "PlacementRelTo": {
                "id": "ee386f0e-fced-11ec-97f5-b3f35fe72359",
                "_e_type": "default::IfcLocalPlacement"
            },
            "RelativePlacement": {
                "Location": null,
                "RefDirection": null,
                "Axis": null,
                "_e_type": "default::IfcAxis2Placement"
            },
            "_e_type": "default::IfcLocalPlacement"
        },
        "Representation": {
            "Name": null,
            "Description": null,
            "Representations": [
                {
                    "RepresentationIdentifier": "Body",
                    "RepresentationType": "SweptSolid",
                    "ContextOfItems": {
                        "ContextIdentifier": null,
                        "ContextType": "Model",
                        "CoordinateSpaceDimension": 3,
                        "Precision": 1e-05,
                        "WorldCoordinateSystem": {
                            "Location": null,
                            "RefDirection": null,
                            "Axis": null,
                            "_e_type": "default::IfcAxis2Placement"
                        },
                        "TrueNorth": {
                            "DirectionRatios": [
                                0,
                                1,
                                0
                            ]
                        },
                        "_e_type": "default::IfcGeometricRepresentationContext"
                    },
                    "Items": [
                        {
                            "SweptArea": {
                                "ProfileType": "AREA",
                                "ProfileName": "IPE400",
                                "Position": null,
                                "OverallWidth": 0.18,
                                "OverallDepth": 0.4,
                                "WebThickness": 0.0086,
                                "FlangeThickness": 0.0135,
                                "FilletRadius": null,
                                "FlangeEdgeRadius": null,
                                "FlangeSlope": null
                            },
                            "Position": {
                                "Location": {
                                    "Coordinates": [
                                        0,
                                        0,
                                        0
                                    ]
                                },
                                "Axis": null,
                                "RefDirection": null
                            },
                            "ExtrudedDirection": {
                                "DirectionRatios": [
                                    0,
                                    0,
                                    1
                                ]
                            },
                            "Depth": 1.5,
                            "Points": []
                        }
                    ]
                },
                {
                    "RepresentationIdentifier": "Axis",
                    "RepresentationType": "Curve3D",
                    "ContextOfItems": {
                        "ContextIdentifier": null,
                        "ContextType": "Model",
                        "CoordinateSpaceDimension": 3,
                        "Precision": 1e-05,
                        "WorldCoordinateSystem": {
                            "Location": null,
                            "RefDirection": null,
                            "Axis": null,
                            "_e_type": "default::IfcAxis2Placement"
                        },
                        "TrueNorth": {
                            "DirectionRatios": [
                                0,
                                1,
                                0
                            ]
                        },
                        "_e_type": "default::IfcGeometricRepresentationContext"
                    },
                    "Items": [
                        {
                            "SweptArea": null,
                            "Position": null,
                            "ExtrudedDirection": null,
                            "Depth": null,
                            "Points": [
                                {
                                    "Coordinates": [
                                        0,
                                        0,
                                        0
                                    ]
                                },
                                {
                                    "Coordinates": [
                                        0,
                                        0,
                                        1.5
                                    ]
                                }
                            ]
                        }
                    ]
                }
            ],
            "_e_type": "default::IfcProductDefinitionShape"
        }
    }
]

Strategies for reducing the general IFC query shape complexity#

Based on the observed results here are some proposals for further improving the efficiency and ergonomics of the query shapes.

IFC specific query modifications#

The following will work only for IFC and would not apply for general query building using ESDL in general.

  • Exclude OwnerHistory in the general query builder and instead create a custom query for exporting all OwnerHistory related information

  • Move ObjectPlacement query out of the general query builder and to its own custom query in order to resolve World Coordinates based on potentially nested RelativePlacement references.

General query modifications#

These options are more general and should apply to all esdl schemas.

  • Do an initial query to return all nested object links and object types without any non-link-property information. In scenarios with objects being referred to multiple times, this strategy will reduce duplication of result data, and should improve things especially with shapes containing a lot of references to same objects.

Next
Slicing in the Spatial Hierarchy
Previous
Query by element name(s)/GlobalId(s)
Copyright © 2022, Kristoffer H. Andersen
Made with Sphinx and @pradyunsg's Furo
On this page
  • Nested Object links
    • Query 1 -> Basic Select
    • Query 2 -> Get properties of linked objects
    • Query 3 -> The completed query shape
    • Strategies for reducing the general IFC query shape complexity
      • IFC specific query modifications
      • General query modifications