Hi,
I want to solve a real problem I have at a client as POC, by using the eventstore projections. Initially the problem was solved by scheduled SP’s, and now a solutions with a bunch of NServiceBus Handlers is used. But I have the impression that it could be solved simpler with the EventStore projections after the course on Oredev.
Problem:
At a client we have energy meters which register many measurement values. Actually we have a value every 15 minutes. This value is used to display graph’s with averages. The averages are updated and stored when an event is received.
We have following averages:
- Average per hour
- Average per day
- Average per month
- Average per year
- Average per HourOfDay
- Average per DayOfWeek
Theoretical solution with ES Projections:
Let assume we have a stream with measurements of following format:
MeasurmentRead
{
DateTime: timestamp
decimal: value
}
- And we have a stream per Meter:
Eg: MeterTV
- The first projection I would apply is to filter the MeasurementRead event to a separated stream. This would create a sub-set that is used as source for the following projections.
Eg: MeasurementRead
- Then I would apply a projection (linkTo) for each type of average to group the measurements per meter for the time frame of the average:
Eg per hour: MeasurementPeriod-MeterTV+H20121108-12
Eg per day: MeasurementPeriod-MeterTV+D20121108
Eg per month: MeasurementPeriod-MeterTV+M201211
Eg per year: MeasurementPeriod-MeterTV+Y2012
Eg per hour of day: MeasurementPeriod-MeterTV+HD01
Eg per day of week: MeasurementPeriod-MeterTV+Monday
It would be nice assign a catorgy to these streams, so that next projection is only applied to streams with that category. The category in this case is ‘MeasurementPeriod’
- Then I would create a projection to create the total and average per stream creation in previous step. The format would look like this:
MeasurmentAverage
{
decimal: total
decimal: average
int: count
}
Eg per hour: MeasurementAverage-MeterTV+H20121108-12
Eg per day: MeasurementAverage-MeterTV+D20121108
Eg per month: MeasurementAverage-MeterTV+M201211
Eg per year: MeasurementAverage-MeterTV+Y2012
Eg per hour of day: MeasurementAverage-MeterTV+HD01
Eg per day of week: MeasurementAverage-MeterTV+Monday
Question:
I’m aware that this create a hugh amount of streams. Are the ES Projections intended to solve this kind of problems?
My Current Implemention:
- Project all MeasurmentRead’s into a single stream
fromAll().when( {
‘MeasurmentRead’: function (s, e) {
if (e.body == null) return s;
linkTo(‘MeasurmentRead’, e);
return s;
}});
No problems with this one…
- Next projection projects the event per Hour Of Day
fromStream(‘MeasurmentRead’)
.whenAny( function(s,e) {
var FormatName = function(when) {
var date = new Date(when);
return date.getHours();
};
if (e.body == null) return s;
var name = ‘MeasurmentPeriod-’ + e.streamId + ‘+H’ + FormatName(e.body.timestamp);
linkTo(name, e);
return s;
});
This also works, the events are grouped per stream and hour of day.
- Calculate totals per time period
So for this one I assume that all stream + eventd created by previous projection are added to ‘MeasurementPeriod’ category.
So I can calculate the total for each MeasurmentPeriod stream
fromCategory(‘MeasurmentPeriod’)
.fromEachStream()
.whenAny( function (s, e) {
if (e.body == null) return s;
var total, count;
var reading = e.body.value;
if (s.data == null)
{
total = reading;
count = 1;
}
else
{
var previous = s.data;
total = previous.total + reading;
count = previous.count + 1;
}
s.data = {
total : e,
count : count,
average: total / count
};
return s;
});
This is where I’m stuck for the moment. The projection does not process the event’s from the previous projection. Only the ‘$stream-created’ event of the projected ‘MeasurmentPeriod’ streams is processed. So actually I would like to process the results of a previous projection (by using category). Is there a wathis a scenario that is be supported? Is there an other way to accomplish this?
Thanks in advance,
Tim