public void FilterTest() { // Creates a basic filter for even-timestamped events, punctuations var inputSubject = new Subject <StreamEvent <int> >(); var qc = new QueryContainer(); var input = qc.RegisterInput(inputSubject, this.disorderPolicy, this.flushPolicy, this.punctuationPolicy, this.completedPolicy); IStreamable <Empty, int> query = input; // Add a no-op operator that isn't simply a batch-in-batch-out operator query = query.ClipEventDuration(IntervalLength); query = query.Where(this.FilterExpression); query = query.ClipEventDuration(IntervalLength); var filtered = qc.RegisterOutput(query).ForEachAsync(o => OnEgress(o)); var process = qc.Restore(); for (int i = 0; i < IngressEventCount; i++) { OnIngress(inputSubject, StreamEvent.CreateInterval(i, i + IntervalLength, i)); if (i > 0 && i % PunctuationGenerationPeriod == 0) { OnIngress(inputSubject, StreamEvent.CreatePunctuation <int>(i)); } // Make sure we don't have any pending events we expected to be egressed at this point Assert.IsTrue(this.expectedOutput.Count == 0); } OnCompleted(inputSubject); }
/// <summary> /// Finds patterns of A on the left followed immediately by B on the right (with no other intermediate events), occurring within (strictly less than) a given time duration /// </summary> /// <typeparam name="TKey">Key type</typeparam> /// <typeparam name="TPayload">Payload type for left input</typeparam> /// <typeparam name="TPayload2">Payload type for right input</typeparam> /// <typeparam name="TResult">Result type</typeparam> /// <param name="stream">Left input stream</param> /// <param name="stream2">Right input stream</param> /// <param name="firstMatch">First element in pattern</param> /// <param name="secondMatch">Second element in pattern</param> /// <param name="resultSelector">Compose result tuple using matching input events</param> /// <param name="withinDuration">Pattern occurs within (strictly less than) given time duration</param> /// <returns>Pattern result stream</returns> public static IStreamable <TKey, TResult> FollowedByImmediate <TKey, TPayload, TPayload2, TResult>( this IStreamable <TKey, TPayload> stream, IStreamable <TKey, TPayload2> stream2, Expression <Func <TPayload, bool> > firstMatch, Expression <Func <TPayload2, bool> > secondMatch, Expression <Func <TPayload, TPayload2, TResult> > resultSelector, long withinDuration) { if (withinDuration >= 0) { // Set match duration if (withinDuration < 2) { throw new Exception("Duration has to be at least 2 chronons"); } if (withinDuration > StreamEvent.MaxSyncTime) { throw new Exception("Duration is too large"); } stream = stream.AlterEventDuration(withinDuration - 1); } // Clip the stream with itself, to make it a signal stream var clippedStream = stream.ClipEventDuration(stream2); // Shift the left side (first match) by one chronon, set right side (second match) to one chronon duration, join to find matches var result = clippedStream.Multicast(xs => xs.Where(firstMatch).ShiftEventLifetime(1).Join(stream2.Where(secondMatch).AlterEventDuration(1), resultSelector)); return(result); }