Zeitstempeländerungen

In den Beispielen in diesem Thema wird gezeigt, wie der Zeitstempel eines Ereignisses mittels Operatoren geändert werden kann. Durch Ändern des Ereigniszeitstempels können Sie die Auswirkung auf Ereignisse auf nachfolgende Vorgänge wie Joins, Aggregationen über Fenstern usw. ändern. Die folgenden LINQ-Erweiterungsmethoden ermöglichen den Zugriff auf diese Funktionen.

Verlagern einer Ereigniszeit

Mit dem ShiftEventTime()-Operator wird die Startzeit jedes Ereignisses im Datenstrom entsprechend dem angegebenen Ausdruck geändert.

Im folgenden Beispiel wird die Zeit jedes Ereignisses im Datenstrom um 15 Minuten in die Zukunft verlagert.

// shift events by 15 minutes into the future.
var shifted = inputStream.ShiftEventTime(e => TimeSpan.FromMinutes(15)); 

Im folgenden Beispiel wird die Zeit jedes Ereignisses im Datenstrom um 1 Stunde in die Vergangenheit verlagert.

// shift events by 1 hour into the past.
var shifted = inputStream.ShiftEventTime(e => TimeSpan.FromHours(-1));

Der Ausdruck zum Angeben der Zeitänderung kann auf die Startzeit des aktuellen Ereignisses verweisen, aber nicht auf die Endzeit oder Nutzlast. Die Verschiebung wirkt sich nicht auf die Lebensdauer oder Nutzlast des Ereignisses aus.

Der DateTime.MinValue-Wert codiert den Zeitwert Minus Unendlich. Weist die Startzeit des Ereignisses diesen Wert auf und wird im angegebenen Ausdruck darauf verwiesen (im Gegensatz zu einer Konstanten), wird der Ausdruck nicht ausgewertet, und die Startzeit bleibt DateTime.MinValue. Ist dies nicht der Fall, wird der Ausdruck zur Laufzeit ausgewertet, was ebenfalls eine Überlaufausnahme auslösen kann.

Beachten Sie, dass die angegebene Zeitänderung auch auf CTI-Ereignisse angewendet wird, die an diesen Operator übergeben werden, da sich ShiftEventTime auf die Startzeiten aller Ereignisse im Datenstrom auswirkt.

Ändern der Dauer eines Ereignisses

Mit dem AlterEventDuration()-Operator wird die Lebensdauer des Ereignisses geändert. Von der Ereignislebensdauer wird das Zeitintervall angegeben, in dem das Ereignis gültig ist. Die Dauer wird als Funktion des Ereignisses definiert, sodass sie aus der Startzeit, Endzeit oder Nutzlast des Ereignisses berechnet werden kann.

Im folgenden Beispiel wird die Ereignisdauer auf 1 Stunde festgelegt.

// set event duration to 1 hour.
var onehour = inputStream.AlterEventDuration(e => TimeSpan.FromHours(1));

Im folgenden Beispiel wird die Ereignisdauer auf das Doppelte der aktuellen Lebensdauer festgelegt.

// double event duration. 
var doubled = inputStream.AlterEventDuration(e => (e.EndTime - e.StartTime) * 2);

Der DateTime.MaxValue-Wert codiert den Zeitwert Plus Unendlich. Weist die Endzeit des Ereignisses diesen Wert auf und wird im angegebenen Ausdruck darauf verwiesen, wird der Ausdruck nicht ausgewertet, und die Endzeit bleibt DateTime.MaxValue.

Ändern sowohl der Verschiebung als auch der Dauer eines Ereignisses

Der AlterEventLifetime()-Operator kombiniert sowohl die AlterEventDuration-Funktion als auch die ShiftEventTime-Funktion für maximale Expressivität.

Der erste Parameter für die AlterEventLifeTime()-Methode gibt den neuen Startzeitstempel an und kann auf die Startzeit des aktuellen Ereignisses verweisen. Dieser Parameter muss als UTC-Zeit angegeben werden. Der zweite Parameter gibt die neue Lebensdauer an und kann auf die Startzeit-, Endzeit- und Nutzlastfelder des aktuellen Ereignisses verweisen.

Im folgenden Beispiel wird die Ereigniszeit um 1 Minute in die Vergangenheit verlagert. Die Endzeit des Ereignisses bleibt jedoch unverändert (durch das Hinzufügen einer zusätzlichen Minute zur ursprünglichen Lebensdauer), wenn die neue Lebensdauer als zweiter Parameter angegeben wird.

// shift event 1 minute into the past, but leave the end time (event duration) unchanged.
var newStream = inputStream.AlterEventLifetime(e => e.StartTime - TimeSpan.FromMinutes(1),
                                               e => e.EndTime - e.StartTime + TimeSpan.FromMinutes(1));]

Beachten Sie, dass die angegebene Startzeitänderung auch auf CTI-Ereignisse angewendet wird, die an diesen Operator übergeben werden.

Nehmen Sie auch die Anmerkungen zu DateTime.MinValue und DateTime.MaxValue früher in diesem Thema zur Kenntnis.

Konvertieren eines Datenstroms in einen Punktereignis-Datenstrom

Der ToPointEventStream-Operator ist eine praktische Funktion zum Konvertieren von Edge- und Intervallereignissen in Punktereignisse (die Lebensdauern der Ereignisse werden in einen Takt nach der Startzeit des Ereignisses geändert), wie im folgenden Beispiel dargestellt.

var pointStream = inputStream.ToPointEventStream();

Wenn Intervallereignisse in Punktereignisse konvertiert werden, wird nur die Startzeit der Ereignisse beibehalten.

Kürzen der Dauer eines Ereignisses

Der ClipEventDuration-Operator nimmt zwei Datenströme als Parameter an und ändert die Lebensdauer von jedem Ereignis im ersten Datenstrom entsprechend der Startzeit des nächsten übereinstimmenden Ereignisses im zweiten Datenstrom.

Bisher wurden Operatoren behandelt, mit denen die Lebensdauer eines Ereignisses durch einen festen Zeitraum geändert werden kann. Der ClipEventDuration-Operator stellt eine sehr flexible Methode zum Anpassen der Lebensdauer von Ereignissen in Bezug auf andere Ereignisse bereit. Im Allgemeinen wird dieser Operator für einen Datenstrom angegeben und nimmt einen anderen Datenstrom als Parameter zusammen mit einer Übereinstimmungsbedingung an. Der Operator kürzt die Lebensdauer jedes Ereignisses im ersten Datenstrom auf die Startzeit des "nächsten" Ereignisses (hinsichtlich der Anwendungszeit) im anderen Datenstrom, das die Übereinstimmungsbedingung erfüllt.

Angenommen, die beiden Datenströme "stream1" und "stream2" tragen Ereignisse mit einem Nutzlastfeld "Id". Die folgende Anweisung kürzt alle Ereignisse in "stream1" auf das nächste Ereignis in "stream2" mit demselben Wert für "Id":

var clipped = stream1.ClipEventDuration(stream2, (e1, e2) => e1.Id == e2.Id);

Die Übereinstimmungsbedingung wird als Ausdruck über beiden Eingabenutzlasten angegeben. Die Semantik dieser Anweisung wird im folgenden Diagramm veranschaulicht:

Semantik von ClipEventDuration

Das Diagramm zeigt, wie das erste Ereignis in "stream1" mit "Id" = A auf das nächste Ereignis mit "Id" = A in "stream2" gekürzt wird. Das andere Ereignis in "stream1" mit "Id" = B wird nicht gekürzt, da das nächste übereinstimmende Ereignis in "stream2" nur nach dem Ende des Ereignisses in "stream1" auftritt.

Dieses Kürzungsverhalten eröffnet eine Vielzahl von Anwendungen. Eine allgemeine Anforderung, die erfüllt werden kann, ist die Konvertierung eines Datenstroms von Punkten in einen Datenstrom kontinuierlicher Intervalle, der auch als "Signal" bezeichnet wird.

Konvertierung von Punkt zu Signal

In diesem Fall müssen Sie zuerst alle Punktereignisse erweitern, damit sie das nächste Ereignis wirklich erreichen. Sie müssen also ein Timeout anwenden, das bestimmt, wie lange ein Ereignis bis zum Auftreten des nächsten Ereignisses dauern soll. Dieses Timeout kann ein begrenzter oder unbegrenzter Zeitraum sein. Angenommen, das Timeout beträgt 60 Sekunden:

var extended = input.AlterEventDuration(e => TimeSpan.FromSeconds(60));

Mit dieser Vorbereitung kann der ClipEventDuration-Operator verwendet werden, der den Datenstrom selbst als Parameter bereitstellt. Dadurch wird jedes Ereignis auf das nächste Ereignis im selben Datenstrom gekürzt und eine kontinuierliche Reihe von Intervallereignissen erstellt. Da nur die Startzeiten des zweiten Datenstroms für das Kürzen von Bedeutung sind, kann auch der ursprüngliche Punktdatenstrom verwendet werden:

var signal = extended.ClipEventDuration(input, (e1, e2) => true);

Hier wird die Übereinstimmungsbedingung immer als "true" ausgewertet, wenn es um einen einzelnen logischen Datenstrom geht. Dies bedeutet, dass alle Ereignisse im Datenstrom einer einzelnen Datenquelle zugeordnet sind.

Die folgenden Diagramme veranschaulichen den Effekt der Punkt-zu-Signal-Konvertierung durch den ClipEventDuration-Operator:

Konvertierung von Punkt zu Signal mit ClipEventDuration

Beide LINQ-Anweisungen können zu einer Anweisung kombiniert werden:

var signal = input.AlterEventDuration(e => TimeSpan.FromSeconds(60)).ClipEventDuration(input, (e1, e2) => true);

Wenn der Datenstrom mehrere logische Datenströme enthält, z. B. Messungen von mehreren Geräten oder die Werte mehrerer Aktien, dann müsste der jeweilige Schlüssel (Geräte-ID oder Aktiensymbol) im booleschen Ausdruck angepasst werden:

var signal = input.AlterEventDuration(e => TimeSpan.FromSeconds(60)).ClipEventDuration(input, (e1, e2) => e1.Symbol == e2.Symbol);

Erstellen von Sitzungen

Ein anderer Anwendungsfall für "ClipEventDuration" ist die Erstellung von Sitzungsereignissen, um während einer Sitzung aufgetretene Ereignisse mit Anmerkungen zu versehen. Ausgegangen wird hier vom folgenden Ereignisschema, das Ereignisse einer Benutzerinteraktion beschreibt:

public class EventType
{
    public int UserId;
    public string Type;
    public DateTime Time;
    public byte[] data;
};

In diesem Beispiel kann der Type des Nutzlastfeldes entweder "start", "end" oder "other" sein und den Start einer Benutzersitzung, das Ende einer Sitzung bzw. Benutzerereignisse während einer Sitzung beschreiben. Das Feld Time enthält den Zeitstempel der Interaktion, und data enthält weitere Informationen. Die Aufgabe besteht darin, jedes Ereignis mit der Startzeit der Sitzung, während der das Ereignis aufgetreten ist, mit Anmerkungen zu versehen. Darüber hinaus wird davon ausgegangen, dass das Timeout jeder Sitzung nach 10 Minuten eintritt.

Das folgende Diagramm zeigt eine Reihe von Beispielereignissen in diesem Szenario:

Erstellen von Sitzungsereignissen mit ClipEventDuration

Zuerst wird die Dauererweiterung gemäß dem Timeout auf alle Ereignisse vom Typ "start" angewendet:

var sessionStarts = from e in input
                    where e.Type == “start”
                    select e;
var sessionStartsExt = sessionStarts.AlterEventDuration(e => TimeSpan.FromMinutes(10));

Danach müssen diese Sitzungsereignisse für jede Benutzer-ID bis zum jeweiligen Ende gekürzt werden:

var sessionEnds = from e in input
                  where e.Type == “end”
                  select e;
var sessions = sessionStartsExt.ClipEventDuration(sessionEnds, (e1, e2) => e1.UserId == e2.UserId);

Das Diagramm veranschaulicht diese Anweisungen:

Kürzen von Sitzungsereignissen mit ClipEventDuration

Jetzt können die Sitzungsereignisse mit den verbleibenden Ereignissen verknüpft werden:

var sessionActivity = from e in input
                      where e.Type == “other”
                      select e;
var annotated = from s1 in sessions
                join s2 in sessionActivity
                on s1.UserId equals s2.UserId
                select new {
                    s2.UserId,
                    s2.Type,
                    s2.Time,
                    s2.Data,
                    SessionStart = s1.Time
                }

Im Join kann auf die sessionActivity-Ereignisse sowie die Felder des Sitzungsereignisses verwiesen werden, sodass das mit Anmerkungen versehene sessionActivity-Ereignis zusammengestellt werden kann, wobei die Sitzungsstartzeit für jedes sessionActivity-Ereignis eingezogen wird:

Verknüpfen von Sitzungsereignissen mit anderen Ereignissen

Da die Joinbedingung die Gleichheit von UserId ist, wird das Ereignis mit UserId=Y in "sessionActivity" nicht für diese bestimmte Sitzung berücksichtigt, in der UserId=X ist.

Die LINQ-Anweisungen können in einen präziseren Satz komprimiert werden:

var sessions = input
                 .Where(e => e.Type == “start”)
                 .AlterEventDuration(e => TimeSpan.FromMinutes(10))
                 .ClipEventDuration(input.Where(e => e.Type == “end”), (e1, e2) => e1.UserId == e2.UserId);
var annotated = from s1 in sessions
                join s2 in input.Where(e => e.Type == “other”)
                on s1.UserId equals s2.UserId
                select new {
                    s2.UserId,
                    s2.Type,
                    s2.Time,
                    s2.Data,
                    SessionStart = s1.Time
                }

Siehe auch

Konzepte

StreamInsight-Serverkonzepte