1

I currently do have a working method which is based on the currently released example code at https://github.com/Azure-Samples/Azure-Time-Series-Insights/tree/master/csharp-tsi-preview-sample

The used types within the following method are created with AutoRest as guided in the GitHub sample: https://github.com/Azure/azure-rest-api-specs/tree/master/specification/timeseriesinsights/data-plane

My initial try is the following:

 public async Task<T> GetLatestEventValue<T>(object[] timeSeriesId, string tsiPropertyName,
     DateTimeRange searchSpan)
 {
     var client = await _tsiClientFactory.GetTimeSeriesInsightsClient();
     var propertyType = GetPropertyType(typeof(T));
     if (propertyType == null) throw new InvalidOperationException($"Unsupported property type (${typeof(T)})");

     string continuationToken = null;
     do
     {
         QueryResultPage queryResponse = await client.Query.ExecuteAsync(
             new QueryRequest(
                 getEvents: new GetEvents(
                     timeSeriesId: timeSeriesId,
                     searchSpan: searchSpan,
                     filter: null,
                     projectedProperties: new List<EventProperty>()
                         {new EventProperty(tsiPropertyName, propertyType)})),
             continuationToken: continuationToken);

         var latestEventIndex = GetLatestEventPropertyIndex(queryResponse.Timestamps);
         var lastValue = queryResponse.Properties
             .FirstOrDefault()
             ?.Values[latestEventIndex];

         if (lastValue != null)
         {
             return (T)lastValue;
         }

         continuationToken = queryResponse.ContinuationToken;
     } while (continuationToken != null);

     return default;
 }

And usage of the method (timeSeriesId is the same as in Microsoft public example):

 var repository = new TsiRepository(_factory);
 object[] timeSeriesId = new object[] { "2da181d7-8346-4cf2-bd94-a17742237429" };
 var today = DateTime.Now;
 var earlierDateTime = today.AddDays(-1);
 var searchSpan = new DateTimeRange(earlierDateTime.ToUniversalTime(), today.ToUniversalTime());
 var result = await repository.GetLatestEventValue<double>(timeSeriesId, "data", searchSpan);

The approach presented above kinda works but does not feel optimal. Is there a simpler way to query the latest event and its value for a given time-series instance? Maybe to take advance of the Time Series Expression (Tsx) capabilities?

ajr
  • 874
  • 2
  • 13
  • 29
  • Have you given a try to [Time Series Expression and syntax](https://learn.microsoft.com/en-us/rest/api/time-series-insights/preview#time-series-expression-and-syntax) – SatishBoddu Jun 01 '20 at 22:11
  • 1
    I did and my posted answer takes advance of the 'TSX'. However, the most speed boost was gained by defining the storage type as 'WarmStorage' – ajr Jun 04 '20 at 11:06

1 Answers1

2

After some time spent searching for the answer, my approach is to take advance of the TSX query syntax for last value and add an additional parameter which decides that the query is run only on the warm storage of TSI. This seems to speed things up nicely. The default is cold storage.

public async Task<T> RunAggregateSeriesLastValueAsync<T>(object[] timeSeriesId, DateTimeRange searchSpan)
{
    var interval = searchSpan.To - searchSpan.FromProperty;
    string continuationToken = null;
    object lastValue;
    do
    {
        QueryResultPage queryResponse = await _tsiClient.Query.ExecuteAsync(
          new QueryRequest(
              aggregateSeries: new AggregateSeries(
              timeSeriesId: timeSeriesId,
              searchSpan: searchSpan,
              filter: null,
              interval: interval,
              projectedVariables: new[] { "Last_Numeric" },
              inlineVariables: new Dictionary<string, Variable>()
              {
                  ["Last_Numeric"] = new NumericVariable(
                  value: new Tsx("$event.value"),
                  aggregation: new Tsx("last($value)"))
              })),
          storeType: "WarmStore", // Speeds things up since only warm storage is used
          continuationToken: continuationToken)
        lastValue = queryResponse.Properties
          .FirstOrDefault()
          ?.Values.LastOrDefault(v => v != null)
        continuationToken = queryResponse.ContinuationToken;
    } while (continuationToken != null)
    return (T)lastValue;
}
ajr
  • 874
  • 2
  • 13
  • 29