UNPKG

@itwin/ecschema-metadata

Version:

ECObjects core concepts in typescript

415 lines • 20.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ECSqlSchemaLocater = void 0; /*--------------------------------------------------------------------------------------------- * Copyright (c) Bentley Systems, Incorporated. All rights reserved. * See LICENSE.md in the project root for license terms and full copyright notice. *--------------------------------------------------------------------------------------------*/ const core_bentley_1 = require("@itwin/core-bentley"); const ECObjects_1 = require("../ECObjects"); const SchemaKey_1 = require("../SchemaKey"); const FullSchemaQueries_1 = require("./FullSchemaQueries"); const IncrementalSchemaLocater_1 = require("./IncrementalSchemaLocater"); const SchemaItemQueries_1 = require("./SchemaItemQueries"); const SchemaParser_1 = require("./SchemaParser"); const SchemaStubQueries_1 = require("./SchemaStubQueries"); const LOGGER_CATEGORY = "IncrementalSchemaLoading.Performance"; /** * An abstract [[IncrementalSchemaLocater]] implementation for loading * EC [Schema] instances from an iModelDb using ECSql queries. * @internal */ class ECSqlSchemaLocater extends IncrementalSchemaLocater_1.IncrementalSchemaLocater { /** * Gets the [[ECSqlSchemaLocaterOptions]] used by this locater. */ get options() { return super.options; } /** * Initializes a new ECSqlSchemaLocater instance. * @param options The options used by this Schema locater. */ constructor(options) { super(options); } /** * Gets the [[SchemaProps]] for the given schema key. This is the full schema json with all elements that are defined * in the schema. The schema locater calls this after the stub has been loaded to fully load the schema in the background. * @param schemaKey The [[SchemaKey]] of the schema to be resolved. * @param context The [[SchemaContext]] to use for resolving references. * @internal */ async getSchemaJson(schemaKey, context) { // If the meta schema is an earlier version than 4.0.3, we can't use the ECSql query interface to get the schema // information required to load the schema entirely. In this case, we fallback to use the ECSchema RPC interface // to fetch the whole schema json. if (!await this.supportPartialSchemaLoading(context)) return this.getSchemaProps(schemaKey); const queryStart = Date.now(); const schemaProps = this.options.useMultipleQueries ? await this.getFullSchemaMultipleQueries(schemaKey, context) : await this.getFullSchema(schemaKey, context); const queryDuration = Date.now() - queryStart; core_bentley_1.Logger.logTrace(LOGGER_CATEGORY, `Recieved SchemaProps for ${schemaKey.name} in ${queryDuration}ms`, { schemaName: schemaKey.name, queryMode: this.options.useMultipleQueries ? "parallel" : "single", duration: queryDuration, }); return schemaProps; } ; /** * Gets the [[SchemaProps]] without schemaItems for the given schema name. * @param schemaName The name of the Schema. * @param context The [[SchemaContext]] to use for resolving references. * @returns * @internal */ async getSchemaNoItems(schemaName, context) { const schemaRows = await this.executeQuery(FullSchemaQueries_1.FullSchemaQueries.schemaNoItemsQuery, { parameters: { schemaName } }); const schemaRow = schemaRows[0]; if (schemaRow === undefined) return undefined; const schema = JSON.parse(schemaRow.schema); const schemaInfos = await this._schemaInfoCache.getSchemasByContext(context) ?? []; return SchemaParser_1.SchemaParser.parse(schema, schemaInfos); } /** * Checks if the [[SchemaContext]] has the right Meta Schema version to support the incremental schema loading. * @param context The schema context to lookup the meta schema. * @returns true if the context has a supported meta schema version, false otherwise. */ async supportPartialSchemaLoading(context) { const metaSchemaKey = new SchemaKey_1.SchemaKey("ECDbMeta", 4, 0, 3); const metaSchemaInfo = await context.getSchemaInfo(metaSchemaKey, ECObjects_1.SchemaMatchType.LatestWriteCompatible); return metaSchemaInfo !== undefined; } ; /** * Gets all the Schema's Entity classes as [[EntityClassProps]] JSON objects. * @param schemaName The name of the Schema. * @param context The [[SchemaContext]] to which the schema belongs. * @returns A promise that resolves to a EntityClassProps array. Maybe empty of no entities are found. * @internal */ async getEntities(schema, context, queryOverride) { const query = queryOverride ?? FullSchemaQueries_1.FullSchemaQueries.entityQuery; return this.querySchemaItem(context, schema, query, "EntityClass"); } /** * Gets all the Schema's Mixin classes as [[MixinProps]] JSON objects. * @param schemaName The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a MixinProps array. Maybe empty of no entities are found. * @internal */ async getMixins(schema, context, queryOverride) { const query = queryOverride ?? FullSchemaQueries_1.FullSchemaQueries.mixinQuery; return this.querySchemaItem(context, schema, query, "Mixin"); } /** * Gets all the Schema's Relationship classes as [[RelationshipClassProps]] JSON objects. * @param schemaName The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a RelationshipClassProps array. Maybe empty if no items are found. * @internal */ async getRelationships(schema, context, queryOverride) { const query = queryOverride ?? FullSchemaQueries_1.FullSchemaQueries.relationshipClassQuery; return this.querySchemaItem(context, schema, query, "RelationshipClass"); } /** * Gets all the Schema's CustomAttributeClass items as [[CustomAttributeClassProps]] JSON objects. * @param schemaName The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a CustomAttributeClassProps array. Maybe empty if not items are found. * @internal */ async getCustomAttributeClasses(schema, context, queryOverride) { const query = queryOverride ?? FullSchemaQueries_1.FullSchemaQueries.customAttributeQuery; return this.querySchemaItem(context, schema, query, "CustomAttributeClass"); } /** * Gets all the Schema's StructClass items as [[StructClassProps]] JSON objects. * @param schemaName The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a StructClassProps array. Maybe empty if not items are found. * @internal */ async getStructs(schema, context, queryOverride) { const query = queryOverride ?? FullSchemaQueries_1.FullSchemaQueries.structQuery; return this.querySchemaItem(context, schema, query, "StructClass"); } /** * Gets all the Schema's KindOfQuantity items as [[KindOfQuantityProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a KindOfQuantityProps array. Maybe empty if not items are found. * @internal */ async getKindOfQuantities(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.kindOfQuantity(true), "KindOfQuantity"); } /** * Gets all the Schema's PropertyCategory items as [[PropertyCategoryProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a PropertyCategoryProps array. Maybe empty if not items are found. * @internal */ async getPropertyCategories(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.propertyCategory(true), "PropertyCategory"); } /** * Gets all the Schema's Enumeration items as [[EnumerationProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a EnumerationProps array. Maybe empty if not items are found. * @internal */ async getEnumerations(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.enumeration(true), "Enumeration"); } /** * Gets all the Schema's Unit items as [[SchemaItemUnitProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a SchemaItemUnitProps array. Maybe empty if not items are found. * @internal */ async getUnits(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.unit(true), "Unit"); } /** * Gets all the Schema's InvertedUnit items as [[InvertedUnitProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a InvertedUnitProps array. Maybe empty if not items are found. * @internal */ async getInvertedUnits(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.invertedUnit(true), "InvertedUnit"); } /** * Gets all the Schema's Constant items as [[ConstantProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a ConstantProps array. Maybe empty if not items are found. * @internal */ async getConstants(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.constant(true), "Constant"); } /** * Gets all the Schema's UnitSystem items as [[UnitSystemProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a UnitSystemProps array. Maybe empty if not items are found. * @internal */ async getUnitSystems(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.unitSystem(true), "UnitSystem"); } /** * Gets all the Schema's Phenomenon items as [[PhenomenonProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a PhenomenonProps array. Maybe empty if not items are found. * @internal */ async getPhenomenon(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.phenomenon(true), "Phenomenon"); } /** * Gets all the Schema's Format items as [[SchemaItemFormatProps]] JSON objects. * @param schema The name of the Schema. * @param context The SchemaContext to which the schema belongs. * @returns A promise that resolves to a SchemaItemFormatProps array. Maybe empty if not items are found. * @internal */ async getFormats(schema, context) { return this.querySchemaItem(context, schema, SchemaItemQueries_1.SchemaItemQueries.format(true), "Format"); } /** * Gets [[SchemaInfo]] objects for all schemas including their direct schema references. * @internal */ async loadSchemaInfos() { const schemaRows = await this.executeQuery(SchemaStubQueries_1.ecsqlQueries.schemaInfoQuery); return schemaRows.map((schemaRow) => ({ alias: schemaRow.alias, description: schemaRow.description, label: schemaRow.label, schemaKey: SchemaKey_1.SchemaKey.parseString(`${schemaRow.name}.${schemaRow.version}`), references: Array.from(JSON.parse(schemaRow.references), parseSchemaReference), })); } /** * Gets the [[SchemaProps]] to create the basic schema skeleton. Depending on which options are set, the schema items or class hierarchy * can be included in the initial fetch. * @param schemaKey The [[SchemaKey]] of the schema to be resolved. * @returns A promise that resolves to the schema partials, which is an array of [[SchemaProps]]. * @internal */ async getSchemaPartials(schemaKey, context) { const queryStart = Date.now(); const itemRows = await this.executeQuery(SchemaStubQueries_1.ecsqlQueries.schemaStubQuery, { parameters: { schemaName: schemaKey.name } }); const queryDuration = Date.now() - queryStart; core_bentley_1.Logger.logTrace(LOGGER_CATEGORY, `Recieved PartialSchema for ${schemaKey.name} in ${queryDuration}ms`, { schemaName: schemaKey.name, itemCount: itemRows.length, duration: queryDuration, }); if (itemRows.length === 0) return undefined; const schemaPartials = []; const addSchema = async (key) => { const stub = await this.createSchemaProps(key, context); schemaPartials.push(stub); if (stub.references) { for (const referenceProps of stub.references) { if (!schemaPartials.some((schema) => schema.name === referenceProps.name)) { await addSchema(SchemaKey_1.SchemaKey.parseString(`${referenceProps.name}.${referenceProps.version}`)); } } } return stub; }; const addItems = async (schemaName, itemInfo) => { let schemaStub = schemaPartials.find((schema) => schema.name === schemaName); if (!schemaStub) { schemaStub = await addSchema(SchemaKey_1.SchemaKey.parseString(`${schemaName}.0.0.0`)); } let items = schemaStub.items; if (!items) { Object.assign(schemaStub, items = { items: {} }); } const existingItem = items[itemInfo.name] || {}; Object.assign(items, { [itemInfo.name]: Object.assign(existingItem, itemInfo) }); }; const reviver = (_key, value) => { return value === null ? undefined : value; }; await addSchema(schemaKey); const schemaInfos = await this._schemaInfoCache.getSchemasByContext(context) ?? []; const stubItems = itemRows.map((itemRow) => { return JSON.parse(itemRow.item, reviver); }); await parseSchemaItemStubs(schemaKey.name, stubItems, addItems, schemaInfos); return schemaPartials; } async querySchemaItem(context, schemaName, query, schemaType) { const start = Date.now(); const itemRows = await this.executeQuery(query, { parameters: { schemaName } }); const queryDuration = Date.now() - start; core_bentley_1.Logger.logTrace(LOGGER_CATEGORY, `Recieved rows of ${schemaType} items for ${schemaName} in ${queryDuration}ms`, { schemaName, itemCount: itemRows.length, itemType: schemaType, duration: queryDuration, }); if (itemRows.length === 0) return []; const items = itemRows.map((itemRow) => { return "string" === typeof itemRow.item ? JSON.parse(itemRow.item) : itemRow.item; }); const schemaInfos = await this._schemaInfoCache.getSchemasByContext(context) ?? []; return await SchemaParser_1.SchemaParser.parseSchemaItems(items, schemaName, schemaInfos) ?? []; } async getFullSchema(schemaKey, context) { const schemaRows = await this.executeQuery(FullSchemaQueries_1.FullSchemaQueries.schemaQuery, { parameters: { schemaName: schemaKey.name } }); const schemaRow = schemaRows[0]; if (schemaRow === undefined) return undefined; // Map SchemaItemRow array, [{item: SchemaItemProps}], to array of SchemaItemProps. const schema = JSON.parse(schemaRow.schema); if (schema.items) { schema.items = schema.items.map((itemRow) => { return itemRow.item; }); } const schemaInfos = await this._schemaInfoCache.getSchemasByContext(context) ?? []; return SchemaParser_1.SchemaParser.parse(schema, schemaInfos); } async getFullSchemaMultipleQueries(schemaKey, context) { const schema = await this.getSchemaNoItems(schemaKey.name, context); if (!schema) return undefined; const items = schema.items || (schema.items = {}); await Promise.all([ this.getEntities(schemaKey.name, context), this.getMixins(schemaKey.name, context), this.getStructs(schemaKey.name, context), this.getRelationships(schemaKey.name, context), this.getCustomAttributeClasses(schemaKey.name, context), this.getKindOfQuantities(schemaKey.name, context), this.getPropertyCategories(schemaKey.name, context), this.getEnumerations(schemaKey.name, context), this.getUnits(schemaKey.name, context), this.getInvertedUnits(schemaKey.name, context), this.getUnitSystems(schemaKey.name, context), this.getConstants(schemaKey.name, context), this.getPhenomenon(schemaKey.name, context), this.getFormats(schemaKey.name, context) ]).then((itemResults) => { const flatItemList = itemResults.reduce((acc, result) => acc.concat(result)); flatItemList.forEach((schemaItem) => { if (!schemaItem.name) { // This should never be happen, as we query the schema items by name from the database, but since the SchemaProps // have name optional, we need the check here to make the compiler happy. throw new Error(`SchemaItem with no name encountered in schema ${schemaKey.name}`); } items[schemaItem.name] = schemaItem; }); }); return schema; } } exports.ECSqlSchemaLocater = ECSqlSchemaLocater; function parseSchemaReference(referenceName) { return { schemaKey: SchemaKey_1.SchemaKey.parseString(referenceName) }; } async function parseSchemaItemStubs(schemaName, itemRows, addItemsHandler, schemaInfos) { if (!itemRows || itemRows.length === 0) { return; } const parseBaseClasses = async (baseClasses) => { if (!baseClasses || baseClasses.length < 2) return; for (let index = baseClasses.length - 1; index >= 0;) { const currentItem = baseClasses[index--]; const baseClassItem = baseClasses[index]; const baseClassName = baseClassItem ? `${baseClassItem.schema}.${baseClassItem.name}` : undefined; const schemaItem = await SchemaParser_1.SchemaParser.parseItem(currentItem, currentItem.schema, schemaInfos); await addItemsHandler(currentItem.schema, { ...schemaItem, name: schemaItem.name, schemaItemType: schemaItem.schemaItemType, baseClass: baseClassName, }); } }; for (const itemRow of itemRows) { const schemaItem = await SchemaParser_1.SchemaParser.parseItem(itemRow, schemaName, schemaInfos); await addItemsHandler(schemaName, { ...schemaItem, name: schemaItem.name, schemaItemType: schemaItem.schemaItemType, mixins: itemRow.mixins ? itemRow.mixins.map(mixin => { return `${mixin.schema}.${mixin.name}`; }) : undefined, }); await parseBaseClasses(itemRow.baseClasses); for (const mixinRow of itemRow.mixins || []) { const mixinItem = await SchemaParser_1.SchemaParser.parseItem(mixinRow, mixinRow.schema, schemaInfos); await addItemsHandler(mixinRow.schema, { ...mixinItem, name: mixinItem.name, schemaItemType: mixinItem.schemaItemType, }); await parseBaseClasses(mixinRow.baseClasses); } } } //# sourceMappingURL=ECSqlSchemaLocater.js.map