Multi-model schema
The SchemaDefinition is designed for a single model. For the purposes of this feature, when the user changes the current model, the SchemaDefinition is reconfigured and the NamedUserCollections for that model are re-mapped, as well as the previous model's collections unmapped.
Identifying a model change#
When the model is change, the app restarts to load the new data and the onConfigLoad function in index.jsx runs. The following code checks if the model has changed:
// Only run schema check if ai resources are set up, ie, check if there is an itemtypes array if (schemaDef?._itemTypes?.length) { // Check if the schema definition is updated for the current model, if not update it if (!await isSchemaDefUpdatedForCurrentModel()) { console.log('Schema definition is not updated for current model, updating now...') await defineSchemaDefForCurrentModel(PlatformApi, selectedModel._id, ctx); } else { console.log('Schema definition is already updated for current model') } }
Get the rvt_element collection related to the current model, then makes sure that it's _itemTypes array contains "RevitElement", which is the collection mapping, and that the other rvt_element collections for other models, do not. If this is not the case, the schema must be redefined and the collections remapped.
const isSchemaDefUpdatedForCurrentModel = async () => { try { let { ctx } = await common.getCurrentProjectInfo(); const currentModel = AppContext.selectedItems?.selectedModel; const resList = await IafItemSvc.getRelatedInItem( currentModel?._id, { query: { _userType: "rvt_elements" } }, ctx ); const currentModelElemColl = resList._list?.[0]; const allElemCollsList = await IafItemSvc.getNamedUserItems({ query: { _userType: "rvt_elements", _itemClass: "NamedUserCollection" } }, ctx );
const currentElemColl = allElemCollsList._list.find( col => col._id === currentModelElemColl?._itemId ); const otherElemColls = allElemCollsList._list.filter( col => col._id !== currentModelElemColl?._itemId ); const otherElemCollsDoNotHaveItemType = otherElemColls.every( col => col._itemTypes[0] !== "RevitElement" ); const currentElemCollHasItemType = currentElemColl._itemTypes ? currentElemColl._itemTypes[0] === "RevitElement" : false; const isUpdated = currentElemCollHasItemType && otherElemCollsDoNotHaveItemType; return isUpdated; } catch (e) { console.log( 'Error checking if schema definition is updated for current model: ', e ); return false; } }
If the model has changed, defining a SchemaDefinition for the current model.
Defining a SchemaDefinition for the current model#
- Generate a new SchemaDefintion using
IafItemSvc.generateSchemaDefinitions:
const schemaDef = await IafItemSvc.generateSchemaDefinitions(ctx);- Reduce the item types array to only distinct item types as only one set is required:
const modelSchemaItemTypes = Object.values( schemaDef._itemTypes.reduce((acc, item) => { acc[item._typeName] = item; return acc; }, {}) );For the purposes of this use case, the related items for the
rvt_element_propscollection require a deeper schema than is generated. For more information on this process, see Constructing a deep related item schema for rvt_element_props.Update the SchemaDefinition with the reduced item types and the deeply defined element properties.
await IafItemSvc.updateSchemaDefinitions( { ...schemaDef, _itemTypes: itemTypesWithFormattedProps }, ctx, undefined );There is no need to alter or reduce the collection defintions in the SchemaDefinition. The AI Agent targets collections based on the mapping applied directly to the NamedUserCollections themselves.
Mapping the current model collections#
- Before mapping the current models collections, remove any previous mapping from the other models' collections by ensuring their
_itemTypesarrays are empty:
const removeItemTypesFromOtherModelColls = async (PlatformApi, modelId, ctx) => { const { IafItemSvc } = PlatformApi; const itemTypesMap = { "rvt_elements": "RevitElement", "rvt_element_props": "RevitElementProps", "rvt_type_elements": "RevitTypeElement" }; try { console.log( "removeItemTypesFromOtherModelColls running for modelId:", modelId ); let modelItems = await IafItemSvc.getRelatedInItem( modelId, {}, ctx ); modelItems = modelItems._list; if (modelItems.length === 0) { throw new Error( "No related items found for the current model to map to schema." ); } const collsToUnmap = modelItems.filter(mi => Object.keys(itemTypesMap).includes(mi._userType) ); const promises = collsToUnmap.map(mi => IafItemSvc.updateNamedUserItem({ _id: mi._itemId, _itemTypes: [] }, ctx) ); await Promise.all(promises); } catch (error) { console.error("Error removing item types from other model collections: " + error); throw error; }};
- For the current model's collections, separate those that require mapping, then map the relevant item type to each collection's
_itemTypesarray and update the collection in the database:
const collsToMap = modelItems.filter(mi => Object.keys(itemTypesMap).includes(mi._userType) );
const promises = collsToMap.map(mi => IafItemSvc.updateNamedUserItem({ _id: mi._itemId, _itemTypes: [itemTypesMap[mi._userType]] }, ctx) ); const updatedColls = await Promise.all(promises);