【原创】StreamInsight查询系列(五)——基本查询操作之用户自定义聚合
上篇博文介绍了StreamInsight基础查询操作中的聚合部分。这篇文章将主要介绍如何在StreamInsight查询中使用用户自定义聚合。
测试数据准备
为了方便测试查询,我们首先准备一个静态的测试数据源:
var weatherData = new[] { new { Timestamp = new DateTime(2010, 1, 1, 0, 00, 00, DateTimeKind.Utc), Temperature = -9.0, StationCode = 71395, WindSpeed = 4}, new { Timestamp = new DateTime(2010, 1, 1, 0, 30, 00, DateTimeKind.Utc), Temperature = -4.5, StationCode = 71801, WindSpeed = 41}, new { Timestamp = new DateTime(2010, 1, 1, 1, 00, 00, DateTimeKind.Utc), Temperature = -8.8, StationCode = 71395, WindSpeed = 6}, new { Timestamp = new DateTime(2010, 1, 1, 1, 30, 00, DateTimeKind.Utc), Temperature = -4.4, StationCode = 71801, WindSpeed = 39}, new { Timestamp = new DateTime(2010, 1, 1, 2, 00, 00, DateTimeKind.Utc), Temperature = -9.7, StationCode = 71395, WindSpeed = 9}, new { Timestamp = new DateTime(2010, 1, 1, 2, 30, 00, DateTimeKind.Utc), Temperature = -4.6, StationCode = 71801, WindSpeed = 59}, new { Timestamp = new DateTime(2010, 1, 1, 3, 00, 00, DateTimeKind.Utc), Temperature = -9.6, StationCode = 71395, WindSpeed = 9}, };
weatherData代表了一系列的天气信息(时间戳、温度、气象站编码以及风速)。
接下去将weatherData转变为点类型复杂事件流:
var weatherStream = weatherData.ToPointStream(Application, t => PointEvent.CreateInsert(t.Timestamp, t), AdvanceTimeSettings.IncreasingStartTime);
用户自定义聚合
问题1:怎样计算每当一个新事件到达时,过去5个事件的平均值?
要实现问题1中的语义,我们需要使用CountByStartTimeWindow,一个可能的写法如下:
var averageQuery = from win in weatherStream .CountByStartTimeWindow(5, CountWindowOutputPolicy.PointAlignToWindowEnd) select new { AverageTemperature = win.Avg(e => e.Temperature), AverageWindspeed = win.Avg(e => e.WindSpeed) };
(from p in averageQuery.ToIntervalEnumerable() where p.EventKind == EventKind.Insert select p).Dump();
然而当我们运行上面程序的时候,会得到下面的运行时异常:
Unhandled Exception: Microsoft.ComplexEventProcessing.InvalidDefinitionException: Microsoft.ComplexEventProcessing.Compiler.CompilerException: The window-based operator 'Aggregate.1.1' contains one or more aggregates that are not supported in count-based windows.
错误大意是计数窗口不支持内置的聚合函数。既然不能使用内置的聚合函数,那么只有自定义了。没错!这就是StreamInsight自定义聚合。
要自定义StreamInsight聚合,用户需要从CepAggregate类(时间不敏感)或CepTimeSensitiveAggregate类(时间敏感)派生出自己的聚合类。对于这里的例子,我们选择与时间不敏感的CepAggregate类派生:
// 定义SimpleAggregate自定义聚合的具体实现,它计算一系列输入元素的平均值 public class SimpleAggregate : CepAggregate<double, double> { public override double GenerateOutput(IEnumerable<double> payloads) { return payloads.Average(); } }
虽然定义好了SimpleAggregate的实现内容,但是我们目前还没法使用它。我们还必须提供LINQ的扩展方法才能允许查询编写者使用用户自定义聚合,如下:
// 定义扩展方法UserDefinedAggregate以使用户自定义聚合函数 // (SimpleAggregate.GenerateOutput)可以被查询使用 public static class UDAExtension { [CepUserDefinedAggregate(typeof(SimpleAggregate))] public static double UserDefinedAggregate<TInput>(this CepWindow<TInput> window, Expression<Func<TInput, double>> map) { throw CepUtility.DoNotCall(); } }
扩展方法是一个签名,它使得查询编写者能够使用用户自定义的聚合并编译查询。
最终的结果包含以下3个事件:
下一篇将介绍StreamInsight基础查询操作中的分组聚合部分。