/// <summary> /// /// </summary> /// <typeparam name="TKey"></typeparam> /// <typeparam name="TOld"></typeparam> /// <typeparam name="TNew"></typeparam> /// <typeparam name="TField1"></typeparam> /// <typeparam name="TField2"></typeparam> /// <param name="source"></param> /// <param name="initializer"></param> /// <param name="fieldSelector1"></param> /// <param name="fieldInitializer1"></param> /// <param name="fieldSelector2"></param> /// <param name="fieldInitializer2"></param> /// <returns></returns> public static IStreamable <TKey, TNew> Select <TKey, TOld, TNew, TField1, TField2>( this IStreamable <TKey, TOld> source, Expression <Func <TNew> > initializer, Expression <Func <TNew, TField1> > fieldSelector1, Expression <Func <TOld, TField1> > fieldInitializer1, Expression <Func <TNew, TField2> > fieldSelector2, Expression <Func <TOld, TField2> > fieldInitializer2) where TNew : new() { Invariant.IsNotNull(source, nameof(source)); Invariant.IsNotNull(initializer, nameof(initializer)); // Validate that the field selector formulas are actually selector expressions if (fieldSelector1.Body.NodeType != ExpressionType.MemberAccess) { throw new ArgumentException("Unable to determine destination field name from selector expression - expected a member selection expression.", nameof(fieldSelector1)); } if (fieldSelector2.Body.NodeType != ExpressionType.MemberAccess) { throw new ArgumentException("Unable to determine destination field name from selector expression - expected a member selection expression.", nameof(fieldSelector2)); } if (!(initializer.Body is NewExpression newExpression)) { throw new ArgumentException("Initializer must return a constructor expression.", nameof(initializer)); } return(source.Select(CreateAdjustColumnsExpression <TOld, TNew>( new Dictionary <string, LambdaExpression> { { ((MemberExpression)(fieldSelector1.Body)).Member.Name, fieldInitializer1 }, { ((MemberExpression)(fieldSelector2.Body)).Member.Name, fieldInitializer2 }, }, newExpression))); }
/// <summary> /// Adjusts the lifetime of incoming events to implement, when used in combination with aggregates, hopping windows. In this implementation each incoming /// event results in a single outgoing event, which means that subsequent aggregates only produce output when the input changes. For instance, if a single /// point event is received and the windowSize, period, and offset are 100, 2, and 0 respectively, a single aggregate output is produced with a lifetime of /// 100 ticks. /// </summary> /// <typeparam name="TKey">Type of (mapping) key in the stream</typeparam> /// <typeparam name="TPayload">Type of payload in the stream</typeparam> /// <param name="source">Input stream</param> /// <param name="windowSize">Window size</param> /// <param name="period">Period (or hop size)</param> /// <param name="offset">Offset from the start of time</param> /// <returns>Result (output) stream</returns> public static IStreamable <TKey, TPayload> HoppingWindowLifetime <TKey, TPayload>( this IStreamable <TKey, TPayload> source, long windowSize, long period, long offset = 0) { if (period > windowSize) { return(source .Select((vs, e) => new { StartTime = vs, Payload = e }) .Where(x => (((x.StartTime + period - offset) % period) > (period - windowSize)) || ((x.StartTime + period - offset) % period == 0)) .Select(x => x.Payload) .AlterEventLifetime(vs => AdjustStartTime(vs, offset, period), windowSize) .SetProperty().IsConstantHop(true, period, offset)); } if (windowSize % period == 0) { return(source.AlterEventLifetime(vs => AdjustStartTime(vs, offset, period), windowSize) .SetProperty().IsConstantHop(true, period, offset)); } else { return(source.AlterEventLifetime( vs => AdjustStartTime(vs, offset, period), vs => AdjustStartTime(vs + windowSize, offset, period) - AdjustStartTime(vs, offset, period)) .SetProperty().IsConstantHop(true, period, offset)); } }
/// <summary> /// /// </summary> /// <typeparam name="TKey"></typeparam> /// <typeparam name="TOld"></typeparam> /// <typeparam name="TNew"></typeparam> /// <param name="source"></param> /// <param name="initializer"></param> /// <param name="newColumnFormulas"></param> /// <returns></returns> public static IStreamable <TKey, TNew> Select <TKey, TOld, TNew>( this IStreamable <TKey, TOld> source, Expression <Func <TNew> > initializer, IDictionary <string, Expression <Func <TOld, object> > > newColumnFormulas = null) where TNew : new() { Invariant.IsNotNull(source, nameof(source)); Invariant.IsNotNull(initializer, nameof(initializer)); // Validate that the dictionary parameter references proper fields if (newColumnFormulas == null) { newColumnFormulas = new Dictionary <string, Expression <Func <TOld, object> > >(); } Type newType = typeof(TNew); foreach (var pair in newColumnFormulas) { if (newType.GetTypeInfo().GetMember(pair.Key).Length == 0) { throw new ArgumentException("Dictionary keys must refer to valid members of the destination type", nameof(newColumnFormulas)); } } if (!(initializer.Body is NewExpression newExpression)) { throw new ArgumentException("Initializer must return a constructor expression.", nameof(initializer)); } return(source.Select(CreateAdjustColumnsExpression <TOld, TNew>( newColumnFormulas.ToDictionary(o => o.Key, o => (LambdaExpression)o.Value), newExpression))); }