【原创】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基础查询操作中的分组聚合部分。

posted @ 2011-08-22 17:37  StreamInsight  阅读(1147)  评论(1编辑  收藏  举报