Instance query
Instance queries allow you to select properties defined in derived classes when selecting a base class or multiple properties with the same name defined in different derived classes.
What is an instance property?
An instance property is any property in a class selected in ECSql or its derived classes accessed via the instance query syntax.
How to access instance properties?
In ECSQL instance properties can be accessed by using the $->
operator.
SELECT $->[CodeValue] FROM [BisCore].[Element] WHERE $->[CodeValue] IS NOT NULL LIMIT 1;
--
SELECT e.$->[CodeValue] FROM [BisCore].[Element] e LIMIT 1;
How it works?
An instance property allows relaxed access to any property within a hierarchy or selected class. It allows full access to the underlying instance of a class using its base class. We can think of it as if $
represents the full instance not just properties of the selected class.
The following ECSQL will return only properties declared in BisCore.Element
SELECT * FROM [BisCore].[Element] WHERE ECInstanceId = 0xc000000014c
ECInstanceId | ECClassId | Model | Last Modified | Code Specification | Code Scope | Code | User Label | Parent | Federation GUID | JSON Properties |
---|---|---|---|---|---|---|---|---|---|---|
0x8000000014c |
0x710 |
{Id:0x80000000003,RelECClassId:0x51} |
2020-09-13T21:03:39.281Z |
{Id:0x1,RelECClassId:0x59} |
{Id:0x1,RelECClassId:0x5b} |
NULL |
Computer |
NULL |
NULL |
NULL |
While the following will return all properties of respective derived class of BisCore.Element
SELECT $ FROM [BisCore].[Element] WHERE ECInstanceId = 0xc000000014c
the above returns one column containing a serialized json instance with all properties
{
"ECInstanceId": "0x8000000014c",
"ECClassId": "0x710",
"Model": {
"Id": "0x80000000003",
"RelECClassId": "0x51"
},
"LastMod": "2020-09-13T21:03:39.281Z",
"CodeSpec": {
"Id": "0x1",
"RelECClassId": "0x59"
},
"CodeScope": {
"Id": "0x1",
"RelECClassId": "0x5b"
},
"UserLabel": "Computer",
"Category": {
"Id": "0x70000000034",
"RelECClassId": "0xa8"
},
"InSpatialIndex": true,
"Origin": {
"X": -20.17197015358312,
"Y": -12.999908317386943,
"Z": -5.363399999999998
},
"Yaw": -9.610521879999869,
"Pitch": 0,
"Roll": 0,
"BBoxLow": {
"X": -0.2844601562499974,
"Y": -0.34431570637657166,
"Z": -0.00034867627660684075
},
"BBoxHigh": {
"X": 0.4287276153476725,
"Y": 0.0297172168743558,
"Z": 0.5207000000000108
},
"GeometryStream": "encoding=base64;Ug==",
"TypeDefinition": {
"Id": "0x80000000145",
"RelECClassId": "0xcc"
},
"TypeId": "382002",
"RevitId": "381840",
"Timestamp": "2020-09-10T13:36:41.000",
"LastModifier": "kiran.patkar",
"ELEM_TYPE_PARAM": "Computer",
"ELEM_CATEGORY_PARAM": "Specialty Equipment",
"FAMILY_LEVEL_PARAM": "B1-CONCOURSE",
"Asset_Tag": "COMPUTER 005"
}
Take a property Asset_Tag
which might be property that exist on some instance of derived hierarchy of bis.Element
and we like to find any instance of Bis.Element
where $->Asset_Tag ='COMPUTER 005'
SELECT [ECInstanceId] FROM [BisCore].[Element] WHERE $->[Asset_Tag] ='COMPUTER 005'
ECInstanceId |
---|
0x8000000014c |
Similarly we can read any set of properties and also filter by them
SELECT $->[RevitId], $->[LastModifier] FROM [BisCore].[Element] WHERE $->[Asset_Tag] ='COMPUTER 005'
$ -> RevitId | $ -> LastModifier |
---|---|
381840 | kiran.patkar |
ECSql will apply a property filter on selected rows such that those instances which have at least one property out of set of instance properties must exist. This improves performance.
SELECT $->[ThisPropertyDoesNotExists] from [BisCore].[Element];
If ThisPropertyDoesNotExists
does not exists in the Bis.Element
derived hierarchy then no row will be returned. ECSql filter only includes rows that must have at least one instance property. If any instance does not have any instance properties requested, then it will will be skipped.
Accessing composite properties like NavigationProperty
, Point2d
, Point3d
or Struct
Only top level instance properties can be accessed via $-><prop>
syntax. Doing something like $->Model.Id
will not work. It might be supported in the future, but currently any access-string within a composite property will return a zero row if it is the only property selected.
The following type of properties can be used directly in filters to return strong type values.
- DateTime
- Integer
- Long
- Binary
- String
- Double
Here is example of RevitId
used with IN()
clause.
SELECT $ from [BisCore].[Element] WHERE $->RevitId In ( 1000, 2000, 3000 );
While composite properties are returned as JSON
.
SELECT $->[Model] from [RevitDynamic].[Computer] where [ECInstanceId] = 0x8000000014c;
above will return following
$ -> Model |
---|
{"Id":"0x80000000003","RelECClassId":"0x51"} |
While following will not return any rows
SELECT $->[Model].[Id] from [RevitDynamic].[Computer] where [ECInstanceId] = 0x8000000014c;
But you can still do following to get child property
SELECT JSON_EXTRACT($->[Model], '$.Id') AS ModelId from [RevitDynamic].[Computer] where [ECInstanceId] = 0x8000000014c;
above will return following
ModelId |
---|
0x80000000003 |
Optional and non-optional instance properties
By default, all properties accessed via instance accessor i.e. $->prop
must exist in the class identifying the row for that row to qualify for output.
If the user uses ?
after a property accessor e.g. $->prop?
then it will be considered optional, and the row class will not be checked to see if the prop
exists.
The following query will return no row if there is no subclass of Bis.Element
which contains both properties CodeValue
and Foo
.
SELECT ECClassId, ECInstanceId
FROM [BisCore].[Element]
WHERE $->CodeValue = 'Profiling' OR $->Foo = 'Hello'
LIMIT 1
On the other hand, the following query makes Foo
optional by adding ?
at the end like $->Foo?
. This will exclude this property from the list of instance properties that must exist in the class of a row for it to qualify for output.
SELECT ECClassId, ECInstanceId
FROM [BisCore].[Element]
WHERE $->CodeValue = 'Profiling' OR $->Foo? = 'Hello'
LIMIT 1
Note: Optional properties may slow down performance while non-optional properties will improve the performance of an instance query.
Examples
-- Instance Access
SELECT $ FROM [BisCore].[Element] WHERE [ECInstanceId] = 0xc000000018a
-- Instance property access
SELECT $->[CodeValue] FROM [bis].[Element] WHERE $->[CodeValue] IS NOT NULL LIMIT 1;
SELECT [e].$->[CodeValue] FROM [bis].[Element] [e] LIMIT 1;
-- Nested select
SELECT * FROM (SELECT $ FROM [meta].[ECClassDef]);
SELECT $ FROM (SELECT * FROM [meta].[ECClassDef]);
-- Instance access in different clauses
SELECT $ FROM [meta].[ECClassDef] WHERE $->[ECInstanceId] < 3;
SELECT $ FROM [meta].[ECClassDef] WHERE $->[ECInstanceId] < 3 ORDER BY $->ECClassId;
SELECT $ FROM [meta].[ECClassDef] WHERE $->[Name] LIKE 'Class%' ORDER BY $->[ECInstanceId] DESC;
SELECT $->[RevitId], $->[LastModifier] FROM [Bis].[Element] WHERE $->[Asset_Tag] ='COMPUTER 005';
SELECT $->[Name] from [meta].[ECClassDef] WHERE $->[ECInstanceId] = 1;
SELECT $ from [Bis].[Element] WHERE $->[RevitId] In ( 1000, 2000, 3000 );
SELECT [ECInstanceId], Name
FROM [meta].[ECClassDef]
WHERE [Name] in (
SELECT $->[Name]
FROM [meta].[ECClassDef]
WHERE $->[ECInstanceId] = 1);
SELECT *
FROM (
SELECT $
FROM [meta].[ECClassDef]
WHERE $->[Schema].[Id] in (
SELECT [Schema].[Id]
FROM [meta].[ECClassDef]
WHERE [Schema].[Id] < 3) ORDER BY $->[ECClassId]);
Limitation
- Only top level property is allowed.
- Only primitive type values can be accessed in the filter directly. Any composite type will require
JSON_EXTRACT()
to extract child value before it can be used in a query. Refer Accessing Composite Properties - Currently indexes are not supported on instance properties.
- MetaData a.k.a
ColumnInfo
is dynamically updated only for primitive properties selected for output. All other properties will get genericColumnInfo
with a string property andextendType=JSON
.
Performance
Generally speaking the performance of instance prop is pretty good, though it involves overhead of extracting either property value or complete instance.
- Use regular properties accessors where possible.
- Do not use instance property access for local properties of class selected.
- Try avoiding filtering queries by instance properties. It could be slow depending on number of rows to which filter will be applied.
Last Updated: 15 May, 2024