Example of a more complicated EventStore projection using GitHub API data
EventStore L&L
- Download EventStore (Mono(SL) vs Windows)
- Running EventStore from the command-line (w/projections)
- Logging in to the UI
- Setting a default ACL (using curl)
- Enabling default projections
- What is a projection?
- Pushing data into EventStore
- TCP vs HTTP
- Streams
- Event Types
- Unique ID’s
- Category streams
- Projections
- Query vs Projection
- Example query
- Count number of events in a stream
- Count number of events for a user (actor) in a stream
- Example projections
- Aggregate streams
- Partition streams
- Iterate category streams (w/foreachStream())
- LinkTo
- Emit
- Debugging a projection
- Other features to check out
- Pure HTTP querying (curl)
- Competing consumers
- C# API
- Other language API's
Available at EventStore Documentation
curl -L -i -d@./settings.js "http://127.0.0.1:2113/streams/%24settings" -H "Content-Type:application/json" -H "ES-EventType: settings" -u "admin:changeit"
curl -i http://127.0.0.1:2113/streams/%24settings -u admin:changeit -H "Accept:application/vnd.eventstore.atom+json"
Reminder to enable default projections.
Writing Data (using GitHubApi)
npm install node-github
curl -i http://127.0.0.1:2113/streams/github-PullRequestEvent -u admin:changeit -H "Accept:application/vnd.eventstore.atom+json"
IntelliSense via 1Prelude.js
/// <reference path="scripts/1Prelude.js" />
// fromAll() will scan ALL events in EventStore (not metadata events)
fromAll()
.whenAny(function(state,event) {
return null;
});
// fromStream only includes events from the named stream
fromStream("github-CreateEvent")
.whenAny(function(state,event) {
return null;
});
// fromCategory includes events from all streams with category name (e.g. <category>-<category-id>)
// nb: .foreachStream() will not work in 'Query' mode as can't specify partition to display
fromCategory("github")
.whenAny(function(state,event) {
return null;
});
// We can filter events EventType using 'when({ .. })'
fromAll()
.when({
"CreateEvent": function(state, event) {
return null;
}
});
// We can use built-in keywords for 'when', such as $init
fromAll()
.when({
$init: function() {
return {}
},
"CreateEvent": function(state, event) {
return null;
}
});
// We can use state through the sequence of events
fromCategory("github")
.when({
$init: function() {
return {
createEventCount: 0,
pullRequestEventCount: 0
}
},
"CreateEvent": function(state, event) {
state.createEventCount++;
return state;
},
"PullRequestEvent": function(state, event) {
state.pullRequestEventCount++;
return state;
}
});
Note that any projections with
linkTo
oremit
needEmit Enabled
ticked!
fromCategory("github")
.when({
$init: function() {
return {
createEventCount: 0,
pullRequestEventCount: 0
}
},
"CreateEvent": function(state, event) {
state.createEventCount++;
return state;
},
"PullRequestEvent": function(state, event) {
state.pullRequestEventCount++;
return state;
}
});
# When queries are put into running projections we can query them from the
# command-line using their projection name.
curl -i http://localhost:2113/projection/<projection-name>/state -u admin:changeit
# Example output
$ curl -i http://localhost:2113/projection/proj.github-category-count/state -u admin:changeit
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTo, ES-ExpectedVersion
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
ES-Position: {"$s":{"$ce-github":334775}}
Cache-Control: max-age=0, no-cache, must-revalidate
Vary: Accept
Content-Type: application/json; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Wed, 25 Nov 2015 16:12:25 GMT
Content-Length: 56
Keep-Alive: timeout=15,max=100
{"createEventCount":39011,"pullRequestEventCount":18612}
// If our projections processes category with .foreachStream() we should be able to query state by category-id
fromCategory('github')
.foreachStream()
.whenAny(function(s,e){
if (s.count === undefined) s.count = 0;
s.count += 1;
return s;
});
# The addition of the 'partition' querystring parameter denotes the category partition
# to return state for.
curl -i http://localhost:2113/projection/<projection-name>/state?partition=<stream-name-incl-category-id> -u admin:changeit
# Example output for category-id "CreateEvent"
$ curl -i http://localhost:2113/projection/proj.github-category-state/state?partition=github-CreateEvent -u admin:changeit
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTo, ES-ExpectedVersion
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
ES-Position: {"$s":{"$ce-github":111332}}
Cache-Control: max-age=0, no-cache, must-revalidate
Vary: Accept
Content-Type: application/json; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Wed, 25 Nov 2015 16:22:37 GMT
Content-Length: 13
Keep-Alive: timeout=15,max=100
{"count":566}
# Example output for category-id "PullRequestEvent"
$ curl -i http://localhost:2113/projection/proj.github-category-state/state?partition=github-PullRequestEvent -u admin:changeit
HTTP/1.1 200 OK
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTo, ES-ExpectedVersion
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
ES-Position: {"$s":{"$ce-github":149738}}
Cache-Control: max-age=0, no-cache, must-revalidate
Vary: Accept
Content-Type: application/json; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Wed, 25 Nov 2015 16:22:47 GMT
Content-Length: 13
Keep-Alive: timeout=15,max=100
{"count":270}
// We can process a sequence of events and push out events into a new stream;
// using 'linkTo' will ensure the new stream event is merely a pointer to the
// original.
//
// This projection creates a stream called "github-all" and puts a link to
// every event from the 'github' category into it.
fromCategory("github")
.whenAny(function(s, e) {
linkTo("githubAll", e);
return s;
});
// We can also 'emit' events, this literally pushes a new event into the (new) stream,
// it can be any event data, or just a copy of the original.
//
// This example partitions data from the github category, where the EventType is 'WatchEvent', and
// creates a new category stream partition by the ID of the person who 'Watched'.
fromCategory('github')
.foreachStream()
.when({
$init: function() {
return { }
},
"WatchEvent": function(state, event) {
emit('githubWatchesBy-' + event.data.sender.id, "WatchesByEvent", event);
return state;
}
});