Using ID-based projections
This page describes how to use ID-based projections to query your data.The simplest and most common way to project an aggregate is to project it by ID. This type of projections are called
single projections. With single projections, the ID of the aggregate is used as the key for the projection by
default, but it is possible to customize the ID by using the idField
property in the projection definition.
Single projections also support filtering using references which can be used for listings as well as indexed fields, which can make projections searchable.
In a single projection, every event for the feed specified in the projection definition will be processed by each handler and the output will be written to the target projection ID, which by default is the aggregate ID of the event being processed.
This page will show how ID-based projections are created and how you can query them from your application.
Creating single ID-based projections
To create a single projection, you need to create a projection definition of type single
. Using our SDKs there is
typed support for creating such a definition, but you can also create the definition manually using the API.
In the following example, a projection called orders-by-id
is created, that will contain all orders in the system. A
new projection will be created whenever an OrderPlaced
event is emitted. The merge
function is used to merge in all
data from the OrderPlaced
event into the projection.
var definition = singleProjection("orders-by-id") .feed("order") .addHandler(newHandler("OrderPlaced") .addFunction(Functions.merge().build()) .build()) .build()projectionClient.createDefinition(definition);
Querying a single projection
To query a single projection, you can use typed methods in the SDKs or use the API directly. You need to provide the projection name and the ID of the projection you want to query.
In the following example, the orders-by-id
projection is queried for an order with
ID 45f184b5-f5a5-4c7e-88c0-5ed246675478
:
var request = ProjectionQueries.single("orders-by-id") .withId("45f184b5-f5a5-4c7e-88c0-5ed246675478") .build(OrderProjection.class);OrderProjection projection = projectionClient.<OrderProjection>query(request).data();
Listing single projections
Since there can be many single projections produced by the same projection definition, it is useful to be able to list all single projections matching a specific name.
In the following example, all projected orders produced by the definition named orders-by-id
are listed:
var request = ProjectionQueries.list("orders-by-id").build(OrderProjection.class);List<OrderProjection> orders = projectionClient.<OrderProjection>query(request).projections() .stream() .map(ProjectionResponse::data) .collect(toList());
Paginating projection list results
To handle scenarios where projection list queries returns many projections, it is possible to paginate through the results. This is done by using Skip and Limit in list query.
Below is an example of how to use skip and limit to paginate through the results for orders-by-id
projection. The
example will skip the first 10 orders and then return at most 20 orders in the response.
var request = ProjectionQueries.list("orders-by-id") .limit(20) .skip(10) .build(OrderProjection.class); List<OrderProjection> orders = projectionClient.<OrderProjection>query(request).projections() .stream() .map(ProjectionResponse::data) .collect(toList());
Deleting a single projection
It is not possible to explicitly delete a single projection. Instead, you should delete the projection by reacting to an
event that should delete the matching projection. Add a corresponding handler to your projection definition and use
the delete
function to delete the projection, if that event occurs for the given projection ID.
In the following example, a projection will be deleted whenever a matching OrderCanceled
event is processed:
// Merge in all data from the OrderPlaced eventvar mergeOnOrderPlaced = newHandler("OrderPlaced").addFunction(Functions.merge().build()).build()// Delete the order projection when an OrderCanceled event occursvar deleteOnOrderCanceled = newHandler("OrderCanceled").addFunction(Functions.delete().build()).build()var definition = singleProjection("orders-by-id") .feed("order") .addHandler(mergeOnOrderPlaced) .addHandler(deleteOnOrderCanceled) .build()projectionClient.createDefinition(definition);