Compartilhar via


Modificações de carimbo de data/hora

Os exemplos deste tópico demonstram o uso de operadores para modificar o carimbo de data/hora de um evento. Ao alterar o carimbo de data/hora do evento, você pode alterar o efeito de eventos em operadores subsequentes, tais como junções, agregações em janelas e assim por diante. Os métodos de extensão LINQ a seguir revelam essa funcionalidade.

Deslocando a hora de um evento

O operador ShiftEventTime() altera a hora de início de cada evento no fluxo de acordo com a expressão especificada.

O exemplo a seguir desloca a hora de cada evento no fluxo antes em 15 minutos no futuro.

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

O exemplo a seguir desloca a hora de cada evento no fluxo em 1 hora retroativamente.

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

A expressão para especificar o deslocamento de hora pode se referir à hora de início do evento atual, mas não à sua hora de término ou carga. O deslocamento não afeta o tempo de vida nem a carga do evento.

O valor DateTime.MinValue é considerado para codificar a um valor de tempo de menos infinito. Se a hora de início do evento tiver este valor e for referida na expressão especificada (como oposto de uma constante), a expressão não será avaliada e a hora de início permanecerá DateTime.MinValue. Se este não for o caso, a expressão será avaliada em tempo de execução, o que pode ainda resultar em uma exceção de estouro.

Observe que a mudança de hora especificada também se aplica a eventos CTI que passam pelo operador porque ShiftEventTime afeta as horas de início de todos os eventos no fluxo.

Alterando a duração de um evento

O operador AlterEventDuration() altera o tempo de vida do evento. O tempo de vida do evento especifica o intervalo de tempo durante o qual o evento é válido. Como a duração é definida como uma função no evento, ela pode ser computada a partir da hora de início, da hora de término ou da carga.

O exemplo a seguir define a duração do evento em 1 hora.

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

O exemplo a seguir define a duração do evento como duas vezes seu tempo de vida atual.

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

O valor DateTime.MaxValue é considerado para codificar a um valor de tempo de mais infinito. Se a hora de término do evento tiver este valor e for referida na expressão especificada (como oposto de uma constante), a expressão não será avaliada e a hora de término permanecerá DateTime.MaxValue.

Alterando o deslocamento e a duração de um evento

O operador AlterEventLifetime() combina as funções AlterEventDuration e ShiftEventTime para obter a expressividade máxima.

O primeiro parâmetro do método AlterEventLifeTime() especifica o novo carimbo de hora de início e pode se referir à hora de início do evento atual. Esse parâmetro deve ser especificado como uma hora UTC. O segundo parâmetro especifica o novo tempo de vida e pode se referir aos campos de hora de início, de hora de término e de carga do evento atual.

O exemplo a seguir desloca o tempo do evento um minuto para trás, mas deixa a hora de término do evento inalterada (adicionando mais um minuto ao tempo de vida original) ao especificar o novo tempo de vida como o segundo parâmetro.

// 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));]

Observe que a mudança da hora de início especificada também se aplica a eventos CTI que passam pelo operador.

Consulte também os comentários referentes a DateTime.MinValue e a DateTime.MaxValue anteriormente neste tópico.

Convertendo um fluxo em um fluxo de eventos pontuais

O operador ToPointEventStream é uma função prática para converter eventos de borda e intervalo em eventos pontuais (alterando os tempos de vida dos eventos em um tique depois da hora de início do evento), conforme mostrado no exemplo a seguir.

var pointStream = inputStream.ToPointEventStream();

Apenas a hora de início dos eventos é mantida quando eventos de intervalo são convertidos em eventos de ponto.

Recortando a duração de um evento

O operador ClipEventDuration leva dois fluxos como parâmetros e altera o tempo de vida de cada evento no primeiro fluxo de acordo com a hora de início do próximo evento correspondente no segundo fluxo.

Até aqui, nós vimos operadores que permitem alterar o tempo de vida de um evento em um período de tempo fixo. O operador ClipEventDuration fornece um método muito flexível de ajustar o tempo de vida dos eventos com respeito a outros eventos. Em geral, este operador é especificado em um fluxo e leva outro fluxo como um parâmetro, junto com uma condição correspondente. O operador recortará o tempo de vida de cada evento no primeiro fluxo para a hora de início do "próximo" evento (em termos de hora de aplicativo) no outro fluxo que preenche a condição correspondente.

Como exemplo, considere dois fluxos, stream1 e stream2, carregando eventos com uma "Id" de campo de carga. A seguinte instrução recorta todos os eventos no stream1 para o próximo evento no stream2 que tem o mesmo valor para "Id":

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

A condição correspondente é fornecida como uma expressão em ambas as cargas de entrada. A semântica desta instrução é ilustrada no seguinte diagrama:

Semântica de ClipEventDuration

O diagrama mostra como o primeiro evento no stream1 com Id = A é recortado para o próximo evento com Id = A no stream2. O outro evento no stream1, onde Id = B, não é recortado, pois o próximo evento correspondente no stream2 só ocorre depois do término do evento no stream1.

Esse comportamento de recorte abre uma ampla gama de aplicativos. Um requisito comum que ele pode preencher é a conversão de um fluxo de pontos em um fluxo de intervalos contínuos, também chamado "sinal".

Conversão de ponto em sinal

Neste caso, primeiro você precisa estender todos os eventos de ponto, de forma que eles realmente atinjam o evento seguinte. Em outras palavras, é necessário aplicar um tempo limite que determine quanto tempo um evento deve durar até que o próximo evento ocorra. Este tempo limite pode ser um intervalo de tempo finito ou infinito. Vamos considerar um tempo limite de 60 segundos:

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

Com esta preparação, podemos usar o operador ClipEventDuration, fornecendo o próprio fluxo como seu parâmetro. Isso fará com que cada evento seja recortado para o próximo no mesmo fluxo, criando uma série contínua de eventos de intervalo. Como apenas as horas de início do segundo fluxo importam para a operação de recorte, também podemos usar o fluxo de ponto original:

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

Aqui, a condição correspondente sempre é avaliada como verdadeira, considerando que estamos vendo um único fluxo lógico, isto é, todos os eventos do fluxo são associados a uma única fonte de dados.

Os seguintes diagramas ilustram o efeito da conversão de ponto-em-sinal por meio do operador ClipEventDuration:

Conversão de ponto em sinal com ClipEventDuration

Ambas as instruções LINQ podem ser combinadas em uma única instrução:

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

Se o fluxo contiver vários fluxos lógicos - por exemplo, medidas de vários dispositivos ou os valores de várias ações - então a chave respectiva (Id de dispositivo ou símbolo de ação) teria que ser correspondida na expressão Booliana:

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

Criando sessões

Outro caso de uso para ClipEventDuration é a criação de eventos de sessão para anotar eventos que ocorreram durante uma sessão. Vamos considerar o seguinte esquema de evento, descrevendo eventos de alguma interação de usuário:

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

Neste exemplo, o campo de carga Type pode ser “início”, “fim” ou “outro”, descrevendo o início de uma sessão de usuário, o fim de uma sessão ou eventos de usuário durante uma sessão, respectivamente. O campo Time contém o carimbo de data/hora da interação e data contém informações adicionais. A tarefa é anotar cada evento com a hora de início da sessão durante a qual o evento ocorreu. Além disso, supomos que o tempo limite de cada sessão expire após 10 minutos.

O seguinte diagrama mostra uma série de eventos de exemplo neste cenário:

Criando eventos de sessão com ClipEventDuration

Primeiro, a expansão da duração de acordo com o tempo limite é aplicada a todos os eventos de tipo "início":

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

Em seguida, esses eventos de sessão precisam ser recortados até seu respectivo fim, para cada Id de usuário:

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

O diagrama ilustra essas instruções:

Recortando eventos de sessão com ClipEventDuration

Agora os eventos de sessão podem ser unidos aos eventos restantes:

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
                }

Na junção, podemos referenciar os eventos sessionActivity bem como os campos do evento de sessão, de forma que podemos montar o evento sessionActivity anotado, trazendo a hora de início de sessão para cada evento sessionActivity:

Unindo eventos de sessão a outros eventos

Desde que a condição de junção é a igualdade de UserId, o evento com UserId=Y em sessionActivity não é levado em conta durante esta sessão específica onde UserId=X.

As instruções LINQ podem ser compactadas em um conjunto mais conciso:

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
                }

Consulte também

Conceitos

Conceitos do servidor StreamInsight