packageDocumentation module import { ECSqlStatement , IModelDb } from "@itwin/core-backend" ; import { assert , DbResult , Id64 , Id64String } from "@itwin/core-bentley" ; import { CategoryDescription , Content , ElementProperties , ElementPropertiesItem , ElementPropertiesPrimitiveArrayPropertyItem , ElementPropertiesPropertyItem , ElementPropertiesStructArrayPropertyItem , IContentVisitor , Item , PresentationError , PresentationStatus , ProcessFieldHierarchiesProps , ProcessMergedValueProps , ProcessPrimitiveValueProps , PropertyValueFormat , StartArrayProps , StartCategoryProps , StartContentProps , StartFieldProps , StartItemProps , StartStructProps , traverseContent , } from "@itwin/presentation-common" ; internal export const buildElementsProperties = ( content ) : ElementProperties [ ] => { if ( ! content || content . contentSet . length === 0 ) return [ ] ; const builder = new ElementPropertiesBuilder ( ) ; traverseContent ( builder , content ) ; return builder . items ; } ; internal export function getElementsCount ( db , classNames ? ) { const filter = createElementsFilter ( "e" , classNames ) ; const query = `
    SELECT COUNT(e.ECInstanceId)
    FROM bis.Element e
    ${ filter ? `WHERE ${ filter }` : "" }
  ` ; return db . withPreparedStatement ( query , ( stmt ) => { return stmt . step ( ) === DbResult . BE_SQLITE_ROW ? stmt . getValue ( 0 ) . getInteger ( ) : 0 ; } ) ; } internal export function * iterateElementIds ( db , classNames ? , limit ? ) { let lastElementId ; while ( true ) { const result = queryElementIds ( db , classNames , limit , lastElementId ) ; yield result . ids ; lastElementId = result . lastElementId ; if ( ! lastElementId ) break ; } } function queryElementIds ( db , classNames ? , limit ? , lastElementId ? ) { function createWhereClause ( ) { const classFilter = createElementsFilter ( "e" , classNames ) ; const elementFilter = lastElementId ? `e.ECInstanceId > ?` : undefined ; if ( ! classFilter && ! elementFilter ) return "" ; if ( classFilter && elementFilter ) return `WHERE ${ classFilter } AND ${ elementFilter }` ; return `WHERE ${ classFilter ?? "" } ${ elementFilter ?? "" }` ; } const query = `
    SELECT e.ECInstanceId elId, eSchemaDef.Name || ':' || eClassDef.Name elClassName
    FROM bis.Element e
    LEFT JOIN meta.ECClassDef eClassDef ON eClassDef.ECInstanceId = e.ECClassId
    LEFT JOIN meta.ECSchemaDef eSchemaDef ON eSchemaDef.ECInstanceId = eClassDef.Schema.Id
    ${ createWhereClause ( ) }
    ORDER BY e.ECInstanceId ASC
  ` ; return db . withPreparedStatement ( query , ( stmt ) => { if ( lastElementId ) stmt . bindId ( 1 , lastElementId ) ; const ids = new Map < string , string [ ] > ( ) ; let currentClassName = "" ; let currentIds = [ ] ; let loadedIds = 0 ; while ( stmt . step ( ) === DbResult . BE_SQLITE_ROW ) { const row = stmt . getRow ( ) ; if ( ! row . elId || ! row . elClassName ) continue ; if ( currentClassName !== row . elClassName ) { currentClassName = row . elClassName ; const existingIds = ids . get ( row . elClassName ) ; if ( ! existingIds ) { currentIds = [ ] ; ids . set ( row . elClassName , currentIds ) ; } else { currentIds = existingIds ; } } currentIds . push ( row . elId ) ; loadedIds ++ ; if ( limit && loadedIds >= limit ) return { ids , lastElementId : row . elId } ; } return { ids , lastElementId : undefined } ; } ) ; } function createElementsFilter ( elementAlias , classNames ? ) { if ( classNames === undefined || classNames . length === 0 ) return undefined ; const classNameRegExp = new RegExp ( /^[\w]+[.:][\w]+$/ ) ; const invalidName = classNames . find ( ( name ) => ! name . match ( classNameRegExp ) ) ; if ( invalidName ) { throw new PresentationError ( PresentationStatus . InvalidArgument , `Encountered invalid class name - ${ invalidName }.
      Valid class name formats: "<schema name or alias>.<class name>", "<schema name or alias>:<class name>"` ) ; } return `${ elementAlias }.ECClassId IS (${ classNames . join ( "," ) })` ; } interface IPropertiesAppender { append ( label , item ) : void ; finish ( ) : void ; } class ElementPropertiesAppender implements IPropertiesAppender { private _propertyItems : { [ label ] : ElementPropertiesItem ; } = { } ; private _categoryItemAppenders : { [ categoryName ] : IPropertiesAppender ; } = { } ; constructor ( private _item , private _onItemFinished ) { } public append ( label , item ) { this . _propertyItems [ label ] = item ; } public finish ( ) { for ( const categoryName in this . _categoryItemAppenders ) { const appender = this . _categoryItemAppenders [ categoryName ] ; appender . finish ( ) ; } this . _onItemFinished ( { class : this . _item . classInfo ?. label ?? "" , id : this . _item . primaryKeys [ 0 ] ?. id ?? Id64 . invalid , label : this . _item . label . displayValue , items : this . _propertyItems } ) ; } public getCategoryAppender ( parentAppender , category ) { let appender = this . _categoryItemAppenders [ category . name ] ; if ( ! appender ) { appender = new CategoryItemAppender ( parentAppender , category ) ; this . _categoryItemAppenders [ category . name ] = appender ; } return appender ; } } class CategoryItemAppender implements IPropertiesAppender { private _items : { [ label ] : ElementPropertiesItem ; } = { } ; constructor ( private _parentAppender , private _category ) { } public append ( label , item ) { this . _items [ label ] = item ; } public finish ( ) { if ( Object . keys ( this . _items ) . length === 0 ) return ; this . _parentAppender . append ( this . _category . label , { type : "category" , items : this . _items } ) ; } } class ArrayItemAppender implements IPropertiesAppender { private _items : ElementPropertiesPropertyItem [ ] = [ ] ; constructor ( private _parentAppender , private _props ) { } public append ( _label , item ) { assert ( item . type !== "category" ) ; this . _items . push ( item ) ; } public finish ( ) { assert ( this . _props . valueType . valueFormat === PropertyValueFormat . Array ) ; if ( this . _props . valueType . memberType . valueFormat === PropertyValueFormat . Primitive ) this . _parentAppender . append ( this . _props . hierarchy . field . label , this . createPrimitivesArray ( ) ) ; else this . _parentAppender . append ( this . _props . hierarchy . field . label , this . createStructsArray ( ) ) ; } private createPrimitivesArray ( ) { return { type : "array" , valueType : "primitive" , values : this . _items . map ( ( item ) => { assert ( item . type === "primitive" ) ; return item . value ; } ) } ; } private createStructsArray ( ) { return { type : "array" , valueType : "struct" , values : this . _items . map ( ( item ) => { assert ( item . type === "struct" ) ; return item . members ; } ) } ; } } class StructItemAppender implements IPropertiesAppender { private _members : { [ label ] : ElementPropertiesPropertyItem ; } = { } ; constructor ( private _parentAppender , private _props ) { } public append ( label , item ) { assert ( item . type !== "category" ) ; this . _members [ label ] = item ; } public finish ( ) { assert ( this . _props . valueType . valueFormat === PropertyValueFormat . Struct ) ; this . _parentAppender . append ( this . _props . hierarchy . field . label , { type : "struct" , members : this . _members } ) ; } } class ElementPropertiesBuilder implements IContentVisitor { private _appendersStack : IPropertiesAppender [ ] = [ ] ; private _items : ElementProperties [ ] = [ ] ; private _elementPropertiesAppender : ElementPropertiesAppender | undefined ; public get items ( ) : ElementProperties [ ] { return this . _items ; } private get _currentAppender ( ) : IPropertiesAppender { const appender = this . _appendersStack [ this . _appendersStack . length - 1 ] ; assert ( appender !== undefined ) ; return appender ; } public startContent ( _props ) { return true ; } public finishContent ( ) { } public processFieldHierarchies ( _props ) { } public startItem ( props ) { this . _elementPropertiesAppender = new ElementPropertiesAppender ( props . item , ( item ) => { this . _items . push ( item ) ; } ) ; this . _appendersStack . push ( this . _elementPropertiesAppender ) ; return true ; } public finishItem ( ) { this . _appendersStack . pop ( ) ; assert ( this . _elementPropertiesAppender !== undefined ) ; this . _elementPropertiesAppender . finish ( ) ; this . _elementPropertiesAppender = undefined ; } public startCategory ( props ) { assert ( this . _elementPropertiesAppender !== undefined ) ; this . _appendersStack . push ( this . _elementPropertiesAppender . getCategoryAppender ( this . _currentAppender , props . category ) ) ; return true ; } public finishCategory ( ) { this . _appendersStack . pop ( ) ; } public startField ( _props ) { return true ; } public finishField ( ) { } public startStruct ( props ) { this . _appendersStack . push ( new StructItemAppender ( this . _currentAppender , props ) ) ; return true ; } public finishStruct ( ) { this . _appendersStack . pop ( ) ! . finish ( ) ; } public startArray ( props ) { this . _appendersStack . push ( new ArrayItemAppender ( this . _currentAppender , props ) ) ; return true ; } public finishArray ( ) { this . _appendersStack . pop ( ) ! . finish ( ) ; } public processMergedValue ( props ) { this . _currentAppender . append ( props . mergedField . label , { type : "primitive" , value : "" } ) ; } public processPrimitiveValue ( props ) { this . _currentAppender . append ( props . field . label , { type : "primitive" , value : props . displayValue ?. toString ( ) ?? "" } ) ; } }