Skip to content

Explain some caveats in change tracking #1798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 37 additions & 15 deletions java/change-tracking.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,39 +173,61 @@ You should consider this when you decide what to include in the identifier.

### Identifiers for Associated Entities

When your entity has an association to an other entity, you might want to log the changes in their relationship.
For compositions, no special annotations are required, the identifiers of the target entity are used instead.

For example, given the following model:

Given the `Orders` entity with an association to a `Customer` instead of the element with customer name:
```cds
entity Orders {
key ID: UUID;
entity Orders : cuid {
OrderNo : String;
customer: Association to Customer;
[...]
items: Composition of many OrderItems on items.parent = $self;
}

entity OrderItems : cuid {
parent : Association to Orders;
supplierName: String;
[...]
quantity : Integer;
}
```

If you annotate such an association with `@changelog`, by default, the change log stores the value of the associated entity key.
If you want, you can store some human-readable identifier instead. You define this by annotating the association with an own identifier:
You can annotate your model as follows to define identifiers for both entities.

```cds
annotate Orders with @changelog: [OrderNo];

annotate OrderItems with @changelog: [
parent.OrderNo,
supplierName,
];
```

Changes for `Orders` and `OrderItems` will have their own respective target or root identifiers. Values of the changed fields are not affected,
such identifiers simply annotate them with additional context. When no annotation is present, identifier is empty.

For associations, you might also replace the changed values with some value of the associated entity instead of the foreign key.

You annotate your entity like this:

```cds
annotate Orders {
customer @changelog: [ customer.name ]
}
```

Elements from the `@changelog` annotation value must always be prefixed by the association name. The same caveats as for the identifiers for the entities apply here.

If you annotate a composition with an identifier, the change log will contain an entry with the identifier's value. Additionally, it will include change log entries for all annotated elements of the composition's target entity.
Elements from the `@changelog` annotation value must always be prefixed by the association name and the identifier of the target entity is not considered at all.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the identifier of the target entity is not considered at all

What is the reason for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was always how I have understood the intention of syntax introduced by Nodies. FK by default (even if association target has own identifier) or own identifier if you wish.
We can no longer look at their syntax I think so this now just documents the way it works.

The same caveats as for the identifiers for the entities apply here.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these caveats?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes in the entity's structure due to redirection + lack of localisation.

Identifiers are not expressions, so they are not rewritten by the compiler. Types can change: annotations must follow this changes. One of the first stakeholders had troubles with this (lot of renaming), that is why I mentioned it.

With our recommended way to model change tracking on top + stable types e.g. codelist this should not happen that much.


:::warning Validation required
If the target of the association is missing, for example, when an entity is updated with the ID for a customer
that does not exists, the changelog entry will not be created. You need to validate
that does not exist, the changelog entry will not be created. You need to validate
such cases in the custom code or use annotations, for example, [`@assert.target`](/guides/providing-services#assert-target).
:::

This feature can also be used for to-many compositions, when you don't need to track the deep changes, but still want to track the additions and removals in the composition.

With association identifiers you also must consider the changes in your entities structure along the projections. In case your target entity is exposed using different projections with removed or renamed elements, you also need to adjust the identifier accordingly in the source entity.
With association identifiers you also must consider the changes in your entities structure along the projections.
In case your target entity is exposed using different projections with removed or renamed elements, you also need to adjust the identifier accordingly.

### Displaying Changes

Expand Down Expand Up @@ -268,10 +290,10 @@ the changes across the complete document and stores them in the change log with
For example, given the order and item model from above, if you change values for the tracked elements with
the deep update, for example, the customer name in the order and the quantity of the item, the change log contains
two entries: one for the order and one for the item. The change log entry for the item will also reflect that
the root of the change is an order.
the root of the change is an order. Both changes will be reachable through the association `changes` of the order entity.

:::warning Prefer deep updates for change tracked entities
If you change the values of the `OrderItems` entity directly via an OData request or a CQL statement, the change log contains only one entry for the item and won't be associated with an order.
If you change the values of the `OrderItems` entity directly via an OData request or a CQL statement, the change log contains only one entry for the item and won't be associated with an order and will not be reachable through the `changes` association. While the updates of composition targets directly is possible, the change tracking feature does not attempt to resolve the parent entity by itself, it requires that either OData request or a CQL statement provide the reference to the parent e.g. `Orders`.
:::

## Reacting on Changes
Expand Down