public IActionResult GenericQuery(GenericTimescaleQueryModel model) { Stopwatch sw = new Stopwatch(); sw.Start(); var conn = new NpgsqlConnection(DbContext.Database.GetDbConnection().ConnectionString); conn.Open(); using (var cmd = conn.CreateCommand()) { //auto bucket size if custom range if (model.Range == QueryRange.CUSTOM) { TimeSpan range = model.End.Value - model.Start.Value; if (range.TotalDays > 365 * 2) { model.BucketType = BucketType.Week; model.BucketSize = 1; model.Limit = -1; } else if (range.TotalDays > 365) { model.BucketType = BucketType.Day; model.BucketSize = 1; model.Limit = -1; } else if (range.TotalDays > 7) { model.BucketType = BucketType.Hour; model.BucketSize = 5; model.Limit = -1; } else if (range.TotalDays > 1) { model.BucketType = BucketType.Hour; model.BucketSize = 1; model.Limit = -1; } else if (range.TotalHours > 1) { model.BucketType = BucketType.Minute; model.BucketSize = 5; model.Limit = -1; } else if (range.TotalMinutes < 60) { model.BucketType = BucketType.Second; model.BucketSize = 5; model.Limit = -1; } } //bucketSize var bucketSizeString = string.Format("{0} {1}", model.BucketSize, model.BucketType.ToString().ToLowerInvariant()); //fields var valueFields = new List <string>(); foreach (var field in model.Metrics) { if (model.Range == QueryRange.LIVE) { valueFields.Add($"{field.Metric} AS {field.Metric}"); } else { valueFields.Add($"{field.Aggregation.ToString()}({field.Metric}) AS {field.Metric}"); } } var valueFieldsString = string.Join(", ", valueFields); //where clause (date range) bool success = QueryRangeHelper.GetDatesOfRange(model.Range, model.Start, model.End, out DateTime dtUTCStart, out DateTime dtUTCEnd); string limitString = model.Limit > 0 ? "LIMIT @limit" : ""; if (model.Range == QueryRange.LIVE) { //real records, no time_buckets cmd.CommandText = string.Format($"SELECT time AS ke, {valueFieldsString} FROM soundusage WHERE time >= @start AND time <= @end ORDER BY ke DESC {limitString};"); } else { cmd.CommandText = string.Format($"SELECT time_bucket_gapfill('{bucketSizeString}', time, '{dtUTCStart.ToString("yyyy-MM-dd")}', '{dtUTCEnd.ToString("yyyy-MM-dd")}') AS ke, {valueFieldsString} FROM soundusage WHERE time >= @start AND time <= @end GROUP BY ke ORDER BY ke DESC {limitString};"); } cmd.CommandType = System.Data.CommandType.Text; if (model.Limit > 0) { cmd.Parameters.AddWithValue("@limit", model.Limit); } cmd.Parameters.AddWithValue("@start", dtUTCStart); cmd.Parameters.AddWithValue("@end", dtUTCEnd); List <GenericQueryRecordViewModel> list = new List <GenericQueryRecordViewModel>(); using (var result = cmd.ExecuteReader()) { while (result.Read()) { List <dynamic> values = new List <dynamic>(); foreach (var metric in model.Metrics) { if (result[result.GetOrdinal(metric.Metric)] == DBNull.Value) { values.Add(null); } else { values.Add(result.GetDouble(result.GetOrdinal(metric.Metric))); } } list.Add(new GenericQueryRecordViewModel() { K = result.GetDateTime(result.GetOrdinal("ke")), Value = values }); } } conn.Close(); //reverse list list = list.OrderBy(a => a.K).ToList(); sw.Stop(); return(Ok(new { Series = model.Metrics.Select(s => s.Metric).ToArray(), Values = list, Duration = sw.Elapsed.TotalMilliseconds })); } }