internal static unsafe TimeSeriesRangeResult GetTimeSeriesRange(DocumentsOperationContext context, string docId, string name, DateTime from, DateTime to, ref int start, ref int pageSize, IncludeDocumentsDuringTimeSeriesLoadingCommand includesCommand = null) { if (pageSize == 0) { return(null); } List <TimeSeriesEntry> values = new List <TimeSeriesEntry>(); var reader = new TimeSeriesReader(context, docId, name, from, to, offset: null); // init hash var size = Sodium.crypto_generichash_bytes(); Debug.Assert((int)size == 32); var cryptoGenerichashStatebytes = (int)Sodium.crypto_generichash_statebytes(); var state = stackalloc byte[cryptoGenerichashStatebytes]; if (Sodium.crypto_generichash_init(state, null, UIntPtr.Zero, size) != 0) { ComputeHttpEtags.ThrowFailToInitHash(); } var initialStart = start; var hasMore = false; DateTime lastSeenEntry = from; includesCommand?.InitializeNewRangeResult(state); foreach (var(individualValues, segmentResult) in reader.SegmentsOrValues()) { if (individualValues == null && start > segmentResult.Summary.NumberOfLiveEntries) { lastSeenEntry = segmentResult.End; start -= segmentResult.Summary.NumberOfLiveEntries; continue; } var enumerable = individualValues ?? segmentResult.Values; foreach (var singleResult in enumerable) { lastSeenEntry = segmentResult.End; if (start-- > 0) { continue; } if (pageSize-- <= 0) { hasMore = true; break; } includesCommand?.Fill(singleResult.Tag); values.Add(new TimeSeriesEntry { Timestamp = singleResult.Timestamp, Tag = singleResult.Tag, Values = singleResult.Values.ToArray(), IsRollup = singleResult.Type == SingleResultType.RolledUp }); } ComputeHttpEtags.HashChangeVector(state, segmentResult.ChangeVector); if (pageSize <= 0) { break; } } var hash = ComputeHttpEtags.FinalizeHash(size, state); TimeSeriesRangeResult result; if (initialStart > 0 && values.Count == 0) { // this is a special case, because before the 'start' we might have values result = new TimeSeriesRangeResult { From = lastSeenEntry, To = to, Entries = values.ToArray(), Hash = hash }; } else { result = new TimeSeriesRangeResult { From = (initialStart > 0) ? values[0].Timestamp : from, To = hasMore ? values.Last().Timestamp : to, Entries = values.ToArray(), Hash = hash }; } includesCommand?.AddIncludesToResult(result); return(result); }
private static Dictionary <string, List <TimeSeriesRangeResult> > GetTimeSeriesRangeResults(DocumentsOperationContext context, string documentId, StringValues names, StringValues fromList, StringValues toList, int start, int pageSize, IncludeDocumentsDuringTimeSeriesLoadingCommand includes) { if (fromList.Count == 0) { throw new ArgumentException("Length of query string values 'from' must be greater than zero"); } if (fromList.Count != toList.Count) { throw new ArgumentException("Length of query string values 'from' must be equal to the length of query string values 'to'"); } if (fromList.Count != names.Count) { throw new InvalidOperationException($"GetMultipleTimeSeriesOperation : Argument count miss match on document '{documentId}'. " + $"Received {names.Count} 'name' arguments, and {fromList.Count} 'from'/'to' arguments."); } var rangeResultDictionary = new Dictionary <string, List <TimeSeriesRangeResult> >(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < fromList.Count; i++) { var name = names[i]; if (string.IsNullOrEmpty(name)) { throw new InvalidOperationException($"GetMultipleTimeSeriesOperation : Missing '{nameof(TimeSeriesRange.Name)}' argument in 'TimeSeriesRange' on document '{documentId}'. " + $"'{nameof(TimeSeriesRange.Name)}' cannot be null or empty"); } var from = string.IsNullOrEmpty(fromList[i]) ? DateTime.MinValue : ParseDate(fromList[i], name); var to = string.IsNullOrEmpty(toList[i]) ? DateTime.MaxValue : ParseDate(toList[i], name); var rangeResult = GetTimeSeriesRange(context, documentId, name, from, to, ref start, ref pageSize, includes); if (rangeResult == null) { Debug.Assert(pageSize <= 0, "Page size must be zero or less here"); return(rangeResultDictionary); } if (rangeResultDictionary.TryGetValue(name, out var list) == false) { rangeResultDictionary[name] = new List <TimeSeriesRangeResult> { rangeResult }; } else { list.Add(rangeResult); } if (pageSize <= 0) { break; } } return(rangeResultDictionary); }