Modifications d'horodatage
Les exemples de cette rubrique illustrent l'utilisation d'opérateurs pour modifier l'horodateur d'un événement. En modifiant l'horodateur d'événement, vous pouvez modifier l'effet des événements sur les opérations suivantes telles que les jointures, les agrégations sur les fenêtres, etc. Les méthodes d'extension LINQ suivantes exposent ces fonctionnalités.
Décalage d'une heure d'événement
L'opérateur ShiftEventTime() modifie l'heure de début de chaque événement dans le flux de données d'après l'expression spécifiée.
L'exemple suivant décale l'heure de chaque événement dans le flux de données de 15 minutes dans le futur.
// shift events by 15 minutes into the future.
var shifted = inputStream.ShiftEventTime(e => TimeSpan.FromMinutes(15));
L'exemple suivant décale l'heure de chaque événement dans le flux de données d'une heure dans le passé.
// shift events by 1 hour into the past.
var shifted = inputStream.ShiftEventTime(e => TimeSpan.FromHours(-1));
L'expression qui spécifie le décalage d'heure peut faire référence à l'heure de début de l'événement actuel, mais pas à son heure de fin ou sa charge utile. Le décalage ne concerne pas la durée de vie ou la charge utile de l'événement.
La valeur DateTime.MinValue encode une valeur de temps de moins l'infini. Si l'heure de début de l'événement a cette valeur et si l'expression spécifiée y fait référence (contrairement à une constante), l'expression n'est pas évaluée et l'heure de début restera DateTime.MinValue. Si tel n'est pas le cas, l'expression est évaluée au moment de l'exécution, ce qui peut toujours engendrer une exception de dépassement de capacité de la pile.
Notez que le décalage d'heure spécifié s'applique également aux événements CTI traités par cet opérateur, car ShiftEventTime a une incidence sur les heures de début de tous les événements dans le flux de données.
Modification de la durée d'un événement
L'opérateur AlterEventDuration() modifie la durée de vie de l'événement. La durée de vie de l'événement spécifie l'intervalle de temps durant lequel l'événement est valide. La durée est définie comme une fonction sur l'événement, afin qu'elle puisse être calculée à partir de l'heure de début, de l'heure de fin ou de la charge utile de l'événement.
L'exemple suivant définit la durée de l'événement sur 1 heure.
// set event duration to 1 hour.
var onehour = inputStream.AlterEventDuration(e => TimeSpan.FromHours(1));
L'exemple suivant définit la durée de l'événement sur deux fois sa durée de vie actuelle.
// double event duration.
var doubled = inputStream.AlterEventDuration(e => (e.EndTime - e.StartTime) * 2);
La valeur DateTime.MaxValue est considérée encoder une valeur de temps de plus l'infini. Si l'heure de fin de l'événement a cette valeur et si l'expression spécifiée y fait référence, l'expression n'est pas évaluée et l'heure de fin restera DateTime.MaxValue.
Modification à la fois du décalage et de la durée d'un événement
L'opérateur AlterEventLifetime() associe les fonctions AlterEventDuration et ShiftEventTime pour une expressivité maximale.
Le premier paramètre de la méthode AlterEventLifeTime() spécifie le nouvel horodatage de début et peut faire référence à l'heure de début de l'événement actuel. Ce paramètre doit être exprimé sous forme d'heure UTC. Le deuxième paramètre spécifie la nouvelle durée de vie et peut se reporter à l'heure de début, à l'heure de fin et aux champs de charge utile de l'événement actuel.
L'exemple suivant décale l'heure de l'événement d'une minute dans le passé, mais conserve l'heure de fin de l'événement (en ajoutant une minute supplémentaire à la durée de vie d'origine) lors de la spécification de la nouvelle durée de vie comme second paramètre.
// 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));]
Notez que le décalage d'heure de début spécifié est également appliqué aux événements CTI traités par cet opérateur.
Voir aussi les remarques relatives à DateTime.MinValue et DateTime.MaxValue plus haut dans cette rubrique.
Conversion d'un flux de données en un flux d'événements point
L'opérateur ToPointEventStream est une fonction pratique pour la conversion d'événements session et intervalle en événements point (en décalant les durées de vie des événements d'un seul cycle après l'heure de début) comme indiqué dans l'exemple suivant.
var pointStream = inputStream.ToPointEventStream();
Seule l'heure de début des événements est conservée lorsque les événements intervalle sont convertis en événements point.
Correspondance de la durée d'un événement
L'opérateur ClipEventDuration prend deux flux de données comme paramètres et modifie la durée de vie de chaque événement du premier flux en fonction de l'heure de début de l'événement correspondant suivant du deuxième flux.
Jusqu'à présent, nous avons étudié des opérateurs qui vous permettent de modifier la durée de vie d'un événement par un intervalle de temps fixe. L'opérateur ClipEventDuration fournit une méthode très flexible d'ajustement de la durée de vie des événements en fonction d'autres événements. En général, cet opérateur est spécifié sur un flux de données et prend un autre flux de données comme paramètre avec une condition de correspondance. L'opérateur fait correspondre la durée de vie de chaque événement du premier flux de données à l'heure de début de l'événement « suivant » (en termes de temps d'application) dans l'autre flux de données qui remplit la condition de correspondance.
À titre d'exemple, supposons deux flux de données, stream1 et stream2, qui acheminent des événements avec un champ de charge utile « Id ». L'instruction suivante fait correspondre tous les événements de stream1 à l'événement suivant de stream2 doté de la même valeur pour « ID » :
var clipped = stream1.ClipEventDuration(stream2, (e1, e2) => e1.Id == e2.Id);
La condition de correspondance est donnée comme expression sur les deux charges utiles d'entrée. La sémantique de cette instruction est illustrée dans le diagramme suivant :
Le diagramme affiche comment le premier événement de stream1 avec Id = A est mis en correspondance avec l'événement suivant avec Id = A de stream2. L'autre événement de stream1, où Id = B, n'est pas mis en correspondance car l'événement correspondant suivant de stream2 se produit uniquement après la fin de l'événement de stream1.
Ce comportement de correspondance ouvre une large gamme d'applications. Une condition courante qu'il peut accomplir est la conversion d'un flux de données de points en un flux de données d'intervalles continus, également appelé « signal ».
Conversion point-à-signal
Dans ce cas, vous devez d'abord étendre tous les événements point afin qu'ils atteignent réellement l'événement suivant. En d'autres termes, vous devez appliquer un délai d'attente qui détermine combien de temps un événement est supposé durer jusqu'à ce que l'événement suivant se produise. Ce délai d'attente peut être un intervalle de temps fini ou infini. Supposons un délai d'attente de 60 secondes :
var extended = input.AlterEventDuration(e => TimeSpan.FromSeconds(60));
Avec cette préparation, nous pouvons utiliser l'opérateur ClipEventDuration en fournissant le flux de données lui-même comme étant son paramètre. Cela entraînera la mise en correspondance de chaque événement au prochain événement du même flux de données, créant ainsi une série continue d'événements intervalle. Étant donné que seules les heures de début du deuxième de flux de données importent pour l'opération de mise en correspondance, nous pouvons également utiliser le flux de point d'origine :
var signal = extended.ClipEventDuration(input, (e1, e2) => true);
Ici, la condition de correspondance renvoie toujours la valeur true, en supposant que nous étudions un flux de données logique unique, c'est-à-dire que tous les événements du flux de données sont associés à une source de données unique.
Les diagrammes suivants illustrent l'effet de la conversion point-à-signal via l'opérateur ClipEventDuration :
Les deux instructions LINQ peuvent être combinées dans une instruction unique :
var signal = input.AlterEventDuration(e => TimeSpan.FromSeconds(60)).ClipEventDuration(input, (e1, e2) => true);
Si le flux de données contient plusieurs flux logiques, par exemple les dimensions de plusieurs périphériques ou les valeurs de plusieurs actions, la clé respective (Id de périphérique ou symbole de cotation) devrait être mise en correspondance dans l'expression booléenne :
var signal = input.AlterEventDuration(e => TimeSpan.FromSeconds(60)).ClipEventDuration(input, (e1, e2) => e1.Symbol == e2.Symbol);
Création de sessions
Un autre cas d'utilisation pour ClipEventDuration est la création d'événements de session pour annoter des événements qui se sont produits pendant une session. Prenons le schéma d'événement suivant en décrivant les événements d'une interaction d'un utilisateur :
public class EventType
{
public int UserId;
public string Type;
public DateTime Time;
public byte[] data;
};
Dans cet exemple, le champ de charge utile Type peut être « start », « end » ou « other », décrivant respectivement le démarrage d'une session utilisateur, la fin d'une session ou des événements d'utilisateur pendant une session. Le champ Time contient l'horodateur de l'interaction et data contient des informations complémentaires. La tâche consiste à annoter chaque événement avec l'heure de début de la session pendant laquelle l'événement s'est produit. De plus, nous supposons que chaque session expire au bout de 10 minutes.
Le diagramme suivant affiche une série d'exemples d'événements dans ce scénario :
En premier lieu, l'expansion de durée d'après le délai d'attente est appliquée à tous les événements de type « start » :
var sessionStarts = from e in input
where e.Type == “start”
select e;
var sessionStartsExt = sessionStarts.AlterEventDuration(e => TimeSpan.FromMinutes(10));
Ensuite, ces événements de session doivent être mis en correspondance jusqu'à leur fin respective, pour chaque ID d'utilisateur :
var sessionEnds = from e in input
where e.Type == “end”
select e;
var sessions = sessionStartsExt.ClipEventDuration(sessionEnds, (e1, e2) => e1.UserId == e2.UserId);
Le diagramme illustre ces instructions :
À présent, les événements de session peuvent être joints aux événements restants :
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
}
Dans la jointure, nous pouvons faire référence aux événements sessionActivity ainsi qu'aux champs de l'événement de session, afin de pouvoir assembler l'événement sessionActivity annoté en étendant l'heure de début de session à chaque événement sessionActivity :
Puisque la condition de jointure est l'égalité d'UserId, l'événement avec UserId=Y dans sessionActivity n'est pas pris en considération pour cette session spécifique où UserId=X.
Les instructions LINQ peuvent être compressées dans un ensemble plus concis :
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
}