Querying Change Summaries

Change Summaries are summaries of changes of ECInstances in an iModel Changeset. Please read Change Summaries first, before doing this section of the tutorial.

Generate the Change Summaries

In case the Change Summaries have not be generated yet for the tutorial's iModel, you have to do that first.

In the iModelConsole run the following commands:

Close the iModel first and re-open it read-write, so that you can generate the Change Summaries:

.close
.open -project:ECSQL -mode:readwrite
.change extractsummaries

Now close the iModel again and re-open it read-only.

.close
.open -project:ECSQL

Attach the Change Summaries

As explained in Change Summaries, you need to attach the file that contains the generated Change Summaries to this iModel by running this command in the iModelConsole:

.change attachcache

Sample Change History

To keep this section as simple as possible, the iModel has three changesets, each with simple changes. They are described here in text to help understand the following queries.

Changeset 1

Operations:

  • Insert a new Device with code DEV-A-G-3 and User Label 'Fire detector'

Result:

ECInstanceId CodeValue UserLabel
0x20000000001 DEV-A-G-3 Fire detector

Changeset 2

Operations:

  • Update the User Label of Device DEV-A-G-3 to 'Fire extinguisher'
  • Insert a new Device with code DEV-A-G-4 and User Label 'Fire detector'

Result:

ECInstanceId CodeValue UserLabel
0x20000000001 DEV-A-G-3 Fire extinguisher
0x20000000002 DEV-A-G-4 Fire detector

Changeset 3

Operations:

  • Delete Device DEV-A-G-3 again

Result:

ECInstanceId CodeValue UserLabel
0x20000000002 DEV-A-G-4 Fire detector

Mining the Change Summaries

There are two main ways to mine Change Summary information:

  1. Find out what classes, what instances, what property values have changed
  2. Find out how property values of certain instances have changed

The following examples will run through both of them.

Explore what has changed

Generally there are three main classes in the ECDbChange ECSchema to explore what has changed:

Additionally the IModelChange ECSchema contains the ChangeSet ECClass that links a Change Summary to the changeset from which it was generated.

The following examples will run through all of them.

What Change Summaries are there

Let's first find some information about the changesets for which Change Summaries have been generated. The query also serves to return the ECInstanceIds of the corresponding Change Summaries which we will need for all other queries.

Try it yourself

Goal: Return the ECInstanceId of the Change Summaries and information about the corresponding changesets, ordered from oldest to newest.

ECSQL

SELECT Summary.Id, WsgId, Description, PushDate, UserCreated, ParentWsgId FROM imodelchange.Changeset ORDER BY PushDate

Result

Summary.Id WsgId Description PushDate UserCreated ParentWsgId
0x6c f7c220138713044a89f4e5fa479564863516b53b Inserted new Device 'DEV-A-G-3'. 2018-12-18T16:03:08.373Z some user id NULL
0x35 9c4239a1fef7cc7136fcef1f6a7472a3b0ffbd7d Fixed user label of Device 'DEV-A-G-3'. Inserted new Device 'DEV-A-G-4'. 2018-12-18T16:03:19.763Z some user id f7c220138713044a89f4e5fa479564863516b53b
0x1 1264417d6364c79d3d1c8d6a45ee6e3ee79188c4 Removed Device 'DEV-A-G-3' again. 2018-12-18T16:03:27.140Z some user id 9c4239a1fef7cc7136fcef1f6a7472a3b0ffbd7d

What instances have changed in a Change Summary

Now that we know what changesets there are, let us look what instances were changed in one of them.

Try it yourself

Goal: Return the id and class id of all instances that have changed in Change Summary 0x35, including the op code for each change.

ECSQL

SELECT ECInstanceId, ChangedInstance.Id, ChangedInstance.ClassId, OpCode FROM ecchange.change.InstanceChange WHERE Summary.Id=0x35

Result

ECInstanceId ChangedInstance.Id ChangedInstance.ClassId OpCode
0x36 0x20000000002 0x100 Insert
0x48 0x20000000001 0x100 Update
0x52 0x20000000002 0x9b Insert
0x57 0x20000000002 0xbd Insert
0x5c 0x20000000002 0x4f Insert
0x61 0x20000000002 0x56 Insert
0x66 0x20000000002 0x58 Insert

For the sake of readability we modify the query by joining to the ECDbMeta ECSchema so that the class names of the changed instances are output.

Try it yourself

Goal: Return the id and class name of all instances that have changed in Change Summary 0x35, including the op code for each change.

ECSQL

SELECT ic.ECInstanceId, ic.ChangedInstance.Id,  s.Name || '.' || c.Name ChangedClass, ic.OpCode FROM ecchange.change.InstanceChange ic
JOIN main.meta.ECClassDef c ON ic.ChangedInstance.ClassId=c.ECInstanceId
JOIN main.meta.ECSchemaDef s ON c.Schema.Id=s.ECInstanceId WHERE ic.Summary.Id=0x35

Result

ECInstanceId ChangedInstance.Id ChangedClass OpCode
0x36 0x20000000002 MyDomain.Device Insert
0x48 0x20000000001 MyDomain.Device Update
0x52 0x20000000002 BisCore.GeometricElement3dIsInCategory Insert
0x57 0x20000000002 BisCore.PhysicalElementIsOfType Insert
0x5c 0x20000000002 BisCore.ModelContainsElements Insert
0x61 0x20000000002 BisCore.CodeSpecSpecifiesCode Insert
0x66 0x20000000002 BisCore.ElementScopesCode Insert

How to read the result of this query

Here is the gist of what you can read from this query:

  • 7 instances were changed in Change Summary 0x35.
  • 1 instance (a Device) was modified in that changeset.
  • 6 instances were added to the iModel in that changeset.
  • The change history's description states that one device was modified and one was added in this changeset. Why are there 5 more changes in this changeset? Inserting a device implicitly sets all its relationships, too. This is what the other 5 changes represent.

What properties have changed for a changed instance

Now that we know that Device 0x36 was modified in Change Summary 0x35 we might want to find out what properties were modified.

Try it yourself

Goal: Return the names of the properties that were modified in the InstanceChange 0x48.

ECSQL

SELECT AccessString FROM change.PropertyValueChange WHERE InstanceChange.Id=0x48

Result

AccessString
LastMod
UserLabel

As we know from the change history's description, the modification of UserLabel is expected. Why was LastMod modified as well? This again is an implicit change, caused by a trigger on the Element.LastMod property which sets it to the current time whenever the element is modified.

Here we used the ECInstanceId 0x48 of the respective InstanceChange object that represents the update of Device 0x20000000001 from the previous query. As always we can use a join, if we only know the id of the changed device, but not the id of the actual change object:

Try it yourself

Goal: Return the names of the properties that were modified in Device 0x20000000001 in Change Summary 0x35.

ECSQL

SELECT AccessString FROM change.PropertyValueChange pc JOIN change.InstanceChange ic ON pc.InstanceChange.Id=ic.ECInstanceId WHERE ic.ChangedInstance.Id=0x20000000001 AND ic.ChangedInstance.ClassId=0x100 AND ic.Summary.Id=0x35

Result

AccessString
LastMod
UserLabel

To deepen the understanding of Change Summaries, let's look why we have to add the AND ic.Summary.Id=0x35 expression to the WHERE clause in the above query. If we forgot to add it, the query would return many more rows than just the expected two. The reason for that is simply that an instance can be changed in different change summaries. In fact, if you look at the change history's description again, we notice that Device 0x20000000001 is modified in all three changesets. First, it is inserted, then it is modified, and finally it is deleted. The query then returns all properties that have changed in all changesets.

Now that we looked at this, let's modify the previous query and use the Change Summary of the last changeset:

Try it yourself

Goal: Return the names of the properties of Device 0x20000000001 that were affected in Change Summary 0x1.

ECSQL

SELECT AccessString FROM change.PropertyValueChange pc JOIN change.InstanceChange ic ON pc.InstanceChange.Id=ic.ECInstanceId WHERE ic.ChangedInstance.Id=0x20000000001 AND ic.ChangedInstance.ClassId=0x100 AND ic.Summary.Id=0x1

Result

AccessString
BBoxHigh.X
BBoxHigh.Y
BBoxHigh.Z
BBoxLow.X
BBoxLow.Y
BBoxLow.Z
Category.Id
CodeScope.Id
CodeSpec.Id
CodeValue
FederationGuid
GeometryStream
InSpatialIndex
LastMod
Model.Id
Origin.X
Origin.Y
Origin.Z
Pitch
Roll
TypeDefinition.Id
TypeDefinition.RelECClassId
UserLabel
Yaw

The query indicates that all properties of the Device were modified in that changeset. Why? Let's quickly run a query that tells us what kind of change this was, i.e. what the OpCode of that change was:

Try it yourself

Goal: Return the OpCode for the change of Device 0x20000000001 in Change Summary 0x1.

ECSQL

SELECT OpCode FROM change.InstanceChange WHERE Summary.Id=0x1 AND ChangedInstance.Id=0x20000000001 AND ChangedInstance.ClassId=0x100

Result

OpCode
Delete

Now we can explain why the query before returned all properties: As Device 0x20000000001 was deleted in that changeset, all its property values were deleted as well. Consequently, the PropertyValueChange ECClass records all properties of the class as being deleted. The same is true for Inserts. You can try that out yourself if you replace the Change Summary id in the previous queries with 0x6c.

Explore how data has changed

In order to see how the property values of instances have changed in a given changeset, the ECSQL function Changes can be used.

Changes Function Syntax

SELECT ... FROM MySchema.MyClass.Changes(ChangeSummaryId, ChangedValueState) ...
  • ChangeSummaryId: The ECInstanceId of the Change Summary.
  • ChangedValueState: corresponds to the values of the enum ChangedValueState.

Walking through the history

Before looking at how the Devices have changed over the time, let's look at the current state.

Try it yourself

Goal: Return id, CodeValue and UserLabel of all Devices.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device

Result

ECInstanceId CodeValue UserLabel
0x10000000020 DEV-A-G-1 NULL
0x10000000022 DEV-A-G-2 NULL
0x10000000025 DEV-A-1-1 NULL
0x10000000028 DEV-A-2-1 NULL
0x1000000002a DEV-A-2-2 NULL
0x1000000002c DEV-A-2-3 NULL
0x1000000002e DEV-A-2-4 NULL
0x10000000030 DEV-A-2-5 NULL
0x10000000032 DEV-A-2-6 NULL
0x10000000034 DEV-A-2-7 NULL
0x10000000036 DEV-A-2-8 NULL
0x20000000002 DEV-A-G-4 Fire detector

Changes in the first changeset

Try it yourself

Goal: Return id, CodeValue and UserLabel of the Devices that were inserted in Change Summary 0x6c.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device.Changes(0x6c,'AfterInsert')

Result

ECInstanceId CodeValue UserLabel
0x20000000001 DEV-A-G-3 Fire detector

This example returns the Devices inserted in the first changeset. The returned Device does no longer exist (as it was deleted in the latest changeset), but you can find it with the help of Change Summaries.

Now let's change the ChangedValueState argument in the query.

Try it yourself

Goal: Return id, CodeValue and UserLabel of the Devices that were updated in Change Summary 0x6c.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device.Changes(0x6c,'AfterUpdate')

Result

ECInstanceId CodeValue UserLabel
no rows

The query not returning anything just means that no Devices were updated in that changeset - which we already know from the change history's description and the previous queries.

Consequently, the same result is obtained when using ChangedValueState.BeforeUpdate and ChangedValueState.BeforeDelete.

Changes in the second changeset

From the previous queries we know that in this changeset a new Device with code DEV-A-G-4 was inserted and an existing one's user label was modified.

Try it yourself

Goal: Return id, CodeValue and UserLabel of the Devices that were inserted in Change Summary 0x35.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device.Changes(0x35,'AfterInsert')

Result

ECInstanceId CodeValue UserLabel
0x20000000002 DEV-A-G-4 Fire detector

When examining updated instances, we can look at the values before and after the update. Note: NULL is returned for CodeValue because it was not affected by this changeset.

Try it yourself

Goal: Return id, CodeValue and UserLabel of the Devices before they were updated in Change Summary 0x35.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device.Changes(0x35,'BeforeUpdate')

Result

ECInstanceId CodeValue UserLabel
0x20000000001 NULL Fire detector

Try it yourself

Goal: Return id, CodeValue and UserLabel of the Devices after they were updated in Change Summary 0x35.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device.Changes(0x35,'AfterUpdate')

Result

ECInstanceId CodeValue UserLabel
0x20000000001 NULL Fire extinguisher

Changes in the third changeset

In the third changeset the Device with code DEV-A-G-3 which was inserted in the first changeset was deleted again.

Try it yourself

Goal: Return id, CodeValue and UserLabel of the Devices before they were deleted in Change Summary 0x1.

ECSQL

SELECT ECInstanceId,CodeValue,UserLabel FROM mydomain.Device.Changes(0x1,'BeforeDelete')

Result

ECInstanceId CodeValue UserLabel
0x20000000001 DEV-A-G-3 Fire extinguisher

< Previous

Last Updated: 20 September, 2019