Physical Model - Tables
Introduction
erDiagram
spatialUnit {
uuid id PK
string name
string type
string description
json unitGuide "'User Guide' for the spatialUnit"
}
bundle {
int id PK
int parentId
string name
json files
json jsonInDBHeader
string description
bool active
}
object {
int bundleId "part of PK"
uuid objectId "part of PK"
string type
string name
array representationIds "array of str(uuid)"
json elementJson "contains the object placement data"
}
object ||--o{ representation : has
representation {
int bundleId "part of PK"
uuid representationId "part of PK"
string type
json elementJson "contains the shape representation data"
geometry geom "geometry column for PostGIS"
}
propertySet {
int bundleId "part of PK"
uuid propertySetId "part of PK"
string name
json elementJson
}
relationship {
int bundleId "part of PK"
uuid relationshipId "part of PK"
string type
string relatingType
uuid relatingId "part of FK"
json elementJson
}
relationship }|..|| object : relating
relationship }|..|| propertySet : relating
relatedMembership {
uuid id PK
int bundleId "part of FK"
uuid relationshipId FK "part of FK"
string objectType
uuid objectId "part of FK"
}
relatedMembership }|--|| relationship : ""
relatedMembership }|--|| object : related
bundleUnit {
uuid5 bundleunit_id PK "The Id as a hash of all columns except unitJson"
int bundle_id "The Id of the bundle"
uuid unit_id "The Id if the Object "
string unitType "The type of the Object (e.g. IfcSpatiaZone)"
string unitName "The name of the object (e.g. Apartment 01-09)"
string relationship_type "The type of the relationship (e.g. 'IfcRelAggregates)"
uuid parent_id "The Id of the Parent"
string parent_type "The type of the parent (e.g. 'IfcBuildingStorey')"
json unitJson "Information that is specific to the type"
}
bundleUnit }o..|{ object : "member"
bundleUnit }|--|| bundle : ""
spatialUnitBundleUnit {
uuid id PK
uuid spatialUnitId
int bundleId FK
uuid bundleUnitEdgeId FK
}
spatialUnitBundleUnit }|--|| spatialUnit : ""
spatialUnitBundleUnit }|--|| bundle: ""
spatialUnitBundleUnit }|--|| bundleUnit: ""
bundleJournal {
uuid id PK
int bundleId FK
json operationJson
}
bundleJournal }|--|| bundle : entry
erDiagram
bundleHistory {
uuid id PK
int bundleId
uuid parentId
string name
string source
string sourceHeader
bool active
}
elementHistory {
uuid id PK
int bundleId
string elementType
uuid elementId
json elementJson
int version
}
spatial_ref_sys table
In PostgreSQL, the table model also contains a table 'spatial_ref_sys' that is created by the PostGIS extension.
the physical model is based on ifcJSON
The model hereunder does not allocate IFC entities to DB tables but allocates ifcJSON entities to DB tables. The ifcJSON entities already aggregate a number of IFC entities. For instance, some ifcJSON relationships contain the data pertaining to one side of the relationship.
The physical model is articulated around simple principles:
- The entities are those provided by the ifc2json transformation
- The the ifc2json transformation is assumed to be lossless with respect to information that matters for Spatial Units; - if not, the transformation must be enhanced
- Items from the ifc2json are distributed in 4 blocks:
- object - applies to all items that are not in one of the 3 other groups
- representation
- propertySet (including IfcPropertySet and IfcElementQuantity)
- relationship
- All information from the ifc2json source is preserved. Each items keeps the totality of its original data into an elementJson field
One of the reasons for this choice is that data in the json can easily be accessed (as a python dictionary, a python pandas DataFrame or as a result of a postgres 'sql', or all combined).
So access is easy, provided that the structure is expected and understood.
Generally speaking, data processing that need to enhance entities or apply algorithms on the entities have an understanding of the data. They can therefore easily access and process the content of the the json.
But other treatments might just not care about these entities and would be confused by details that they don't need.
Tables
All tables details can be found under src/model with their pydantic definition.
Spatial Unit
The Spatial Unit acts as a relay for the Spatial Unit in the external adminirative system. The other tables will reference the id of this Spatial Unit which is turn will - when needed or convenient -- hold an external id for the external Spatial Unit.
Bundle
The bundle (elsewhere referred to as the dataBundle) is the logical 'bucket' for all files and data related to a 'source' project (whether IFC or not). It maintains a list of all files processed in relationship to the bundle.
Bundle Journal
The bundle journal keeps track of all operations that have been performed in relation with the bundle. This can be e.g.,
- import the source IFC
- transform the IFC in an elementJson
- filter the elementJson
- store the elementJson content in the database
Bundle Unit
The table stores the graph structure and the groups of entities that can be referred to by a Spatial Unit, namely the Site(s), Building(s), Storey(s), Space(s), Zone(s) and Spatial Zone(s). The same unit can be present multiple times. For instance, a 'Kitchen' space may be linked to the 'Apartment' SpatialZone to which it belongs via an 'IfcRelReferencedInSpatialStructure' relationship while being also linked to the containing 'Storey' via an 'IfcRelAgreggates' relationship.
The Id/PK (bundleunit_id) is based on a hash of all columns except the unitJson (and the technical data such as 'created_at' and 'updated_at'). The idea is that an update applies to the combination of these elements, and if one of them changes it must be an 'insert' or a 'delete old' + 'insert new'.
Spatial Unit Bundle Unit (junction between a spatial unit and a bundle unit)
Makes the junction between a Spatial Unit and a BundleIUnit.
Bundle Membership included in the primary key of elements
We don't use a junction table between the bundle and the elements of the bundle (object, representation, propertySet and relationship). Instead, we use the bundleId in the primary key of its elements (object, representation, propertySet, relationship) so that the primary key of an element is (bundleId, elementId).
For the elementId's we prefer to keep the uuid provided by the IFC GUID (globalId) in its extended form.
This GUID is technically unique but may be used repeatedly in different contexts. For instance, an IfcWall with a given GUID could appear for that wall in distinct IFC's for the design, construction and as-built phases. And there could be variations in the IfcWall data.
Also the discussion on the buildingSMART forum about GUIDs in an BIM Project brings additional perspective on the use of the guid: in a federation of IFC from distinct disciplines, the same GUID could be used for the given IfcWall but with a different use.
Therefore, the GUID will bear a unicity property only within a given bundle and a convenient unique key will be provided by (bundleId, elementId) dispensing from using a junction table.
Also, comparison across bundles could help identify variations in a specific element or help in federating different views, but with no guarantee as made clear in the discussion GUIDs in an BIM Project where an example is given of a project with multiple disciplines using different tools and producing distincts GUIDs for the same element.
Object
Contains all of the items not in Represention, PropertySet or Relationship.
Representation
Contains the items for the products that have one or more shape representation(s) (support for the 3D rendering and for measurement). Representations are separated from the product themselves which are in the object table. The separation is mainly for data management purpose: not all processes need the representation and several applications / model sources do not have shape representations. The representation table has a 'Geometry' column that is intended to support geometry related functions.
To be challenged: the local placement is in the Object table and the geometry in the Representation table.
PropertySet
Contains the propertySets and elementQuantities that apply to one or more other items.
Relationship
All types of Relationships are stored in the same table with a structure that account for
- the type of the relationship
- the id of the unique relating item
- the type of the relating item
- the elementJson of the source relationship
The list of the related items is included in the elementJson and is also expanded in the Related Membership table.
When applying a filter on the import (or when creating a dependent bundle), some elements in the original IFC will be filtered out and will not remain in the bundle. This means that a relationship with a relating element in the the bundle may originally contain references to related elements that have been filtered out. In that case, the relationship is pruned for the references to the missing related elements. Therefore the database may contain different versions of a relationship with the same globalId (uuid of the relationship). To access the version of the relationship relevant for a bundle, it is necessary to give both the bundleId and the relationshipId.
The column for the relating_id may be null. Indeed for IfcRelAssociateMaterial, the relating material is in the relationship itself.
Related Membership
The table expand the relationships with one row per related item in de relationship. This is more convenient for SQL based queries.
As noted hereinabove the related membership exist for a combination of a bundleId and relationshipId (globalId of the relationship).
For some relationships, there are no related item. This is the case for IfcRelSpaceBoundary for a virtual boundary.
Bundle History
If a bundle is dropped,
- the detailed data pertaining the bundle (object, relationhips, ...) are deleted
- the summary data of that bundle is moved to the history table. Therefore, the links to the source and transformed file(s) is maintained
This is not activated and tested at this stage. It is more a placeholder to remember to address this topic more in depth
Elements History
The idea is that each time an element is modified, the before update version is saved in this history.
This is not activated and tested at this stage. It is more a placeholder to remember to address this topic more in depth