To create useful projections we need to merge the event data with the projection data in different ways. We provide templating support built on JsonPath to support this out-of-the-box.
If the templating support we provide is too restricted for your use-case we encourage you to write your own projector, deployed either as an external function (hosted on eg. AWS) or external service using the Feed API and a storage of your choice. Read more about customized projectors in the next chapter.
The structure of a handler using the provided JsonPath templating looks like this:
{"eventType": "<YOUR_EVENT_TYPE>","functions": [{"function": "merge","targetSelector": "$.projection","eventSelector": "$.event"}]}
This example will merge the content of the incoming event by using the JsonPath selector $.event
with the current state of the projection by targeting the full projection state using the JsonPath selector $.projection
.
In a handler function you always have access to $.event
(the current event being handled) and $.projection
(the current projection state).
Besides that, there is also a $.metadata
struct containing the fields aggregateId
, timestamp
, createdAt
and updatedAt
.
The above example can also be simplified to:
{ "function": "merge" }
since the default values for targetSelector
and eventSelector
are $.event
and $.projection
, respectively.
If you have a static value that you want to use as the data for the projection instead of some value that is provided in the event, you can use the rawData
argument instead of the eventSelector
. You can then provide any JSON value which will be used as-is.
rawData
and eventSelector
are always mutual exclusive, so make sure you only provide one of them in your function configuration.
Merges event data with existing projected data.
Argument | Evaluated Type | Required |
targetSelector | Object | Yes |
eventSelector | Object | No |
targetFilter | Not used | No |
eventFilter | Not used | No |
rawData | Object | No |
{"eventType": "OrderStatusChangedEvent","functions": [{"function": "merge"}]}
Sets/replaces the value of an existing key.
Argument | Evaluated Type | Required |
targetSelector | Any | Yes |
eventSelector | Any | No |
targetFilter | Filter expression | No |
eventFilter | Filter expression | No |
rawData | Any | No |
{"eventType": "EmailUpdatedEvent","functions": [{"function": "set","targetSelector": "$.projection.user.email","eventSelector": "$.event.newEmail"}]}
Removes an existing key.
Argument | Evaluated Type | Required |
targetSelector | Any | Yes |
eventSelector | Not used | No |
targetFilter | Filter expression | No |
eventFilter | Filter expression | No |
rawData | Not used | No |
{"eventType": "EmailRemovedEvent","functions": [{"function": "unset","targetSelector": "$.projection.user.email"}]}
Clear the entire projection or a specific field, i.e reset it to a default value.
Data type | Default value |
String |
|
Boolean |
|
Number |
|
Array |
|
Object |
|
Argument | Evaluated Type | Required |
targetSelector | Any | No |
eventSelector | Not used | No |
targetFilter | Filter expression | No |
eventFilter | Filter expression | No |
rawData | Not used | No |
{"eventType": "ListClearedEvent","functions": [{"function": "clear","targetSelector": "$.projection.todoList"}]}
Delete a projection instance entirely. Future requests will result in a 404 Not Found.
Argument | Evaluated Type | Required |
targetSelector | Not used | No |
eventSelector | Not used | No |
targetFilter | Not used | No |
eventFilter | Filter expression | No |
rawData | Not used | No |
{"eventType": "UserDeletedEvent","functions": [{"function": "delete"}]}
Adds anything to the end of an array.
Argument | Evaluated Type | Required |
targetSelector | Array | Yes |
eventSelector | Any | No |
targetFilter | Filter expression | No |
eventFilter | Filter expression | No |
rawData | Any | No |
{"eventType": "RunnerFinishedRaceEvent","functions": [{"function": "append","targetSelector": "$.projection.finishers","eventSelector": "$.event['runnerName','finishTime']"}]}
Adds anything to the beginning of an array.
Argument | Evaluated Type | Required |
targetSelector | Array | Yes |
eventSelector | Any | No |
targetFilter | Filter expression | No |
eventFilter | Filter expression | No |
rawData | Any | No |
{"eventType": "TodoAddedEvent","functions": [{"function": "prepend","targetSelector": "$.projection.todos","eventSelector": "$.event['todoId','todoText']"}]}
Removes an existing key or an array element matching filter expression.
Argument | Evaluated Type | Required |
targetSelector | Any | Yes |
eventSelector | Not used | No |
targetFilter | Filter expression | Yes |
eventFilter | Not used | No |
rawData | Not used | No |
{"eventType": "TodoRemovedEvent","functions": [{"function": "remove","targetSelector": "$.projection.todos[?]","targetFilter": "[?(@.todoId == $.event.todoId)]"}]}
Sums two numbers together.
Argument | Evaluated Type | Required |
targetSelector | Number | Yes |
eventSelector | Number | No |
targetFilter | Not used | No |
eventFilter | Not used | No |
rawData | Number | No |
{"eventType": "OrderPlacedEvent","functions": [{"function": "add","targetSelector": "$.projection.totalAmount","eventSelector": "$.event['orderAmount']"}]}
Subtracts two numbers.
Argument | Evaluated Type | Required |
targetSelector | Number | Yes |
eventSelector | Number | No |
targetFilter | Not used | No |
eventFilter | Not used | No |
rawData | Number | No |
{"eventType": "OrderRefundedEvent","functions": [{"function": "subtract","targetSelector": "$.projection.totalAmount","eventSelector": "$.event['orderAmount']"}]}
Increases a number by one.
Argument | Evaluated Type | Required |
targetSelector | Number | Yes |
eventSelector | Not used | No |
targetFilter | Filter expression | No |
eventFilter | Not used | No |
rawData | Not used | No |
{"eventType": "TicketReservedEvent","functions": [{"function": "inc","targetSelector": "$.projection.ticketCount"}]}
Decreases a number by one.
Argument | Evaluated Type | Required |
targetSelector | Number | Yes |
eventSelector | Not used | No |
targetFilter | Filter expression | No |
eventFilter | Not used | No |
rawData | Not used | No |
{"eventType": "TicketReleasedEvent","functions": [{"function": "dec","targetSelector": "$.projection.ticketCount"}]}
You can make your projections
Marks a projection field as a reference which makes it filterable in the API.
Argument | Evaluated Type | Required |
targetSelector | String/Number/Date | Yes |
eventSelector | Not used | No |
targetFilter | Not used | No |
eventFilter | Not used | No |
rawData | Not used | No |
{"eventType": "TicketReleasedEvent","functions": [{"function": "setref","targetSelector": "$.projection.releaseDate"}]}
Clears the reference for the projection, removing the ability to filter.
Argument | Evaluated Type | Required |
targetSelector | Not used | No |
eventSelector | Not used | No |
targetFilter | Not used | No |
eventFilter | Not used | No |
rawData | Not used | No |
{"eventType": "TicketReleasedEvent","functions": [{"function": "clearref"}]}
Functions can also provide two filters: targetFilter
and eventFilter
.
To add a filter to a selector you provide a [?]
in the selector text, as described in the JsonPath documentation. The filter for the selector is then applied for the given function. This is useful for matching on ids in nested lists or to apply conditional logic for when/how to process events.
All Filter Operators that are described here are supported.