diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index df8127a..bb4934c 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -1,7 +1,6 @@ using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Services.Interfaces; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Net; @@ -11,15 +10,15 @@ namespace DD.Persistence.API.Controllers; /// Хранение наборов данных с отметкой времени. /// [ApiController] -[Authorize] +//[Authorize] [Route("api/[controller]")] public class TimestampedValuesController : ControllerBase { private readonly ITimestampedValuesService timestampedValuesService; - public TimestampedValuesController(ITimestampedValuesService service) + public TimestampedValuesController(ITimestampedValuesService repository) { - this.timestampedValuesService = service; + this.timestampedValuesService = repository; } /// @@ -41,26 +40,7 @@ public class TimestampedValuesController : ControllerBase /// /// Получение данных с фильтрацией. Значение фильтра null - отключен /// - /// Дискриминатор (идентификатор) набора - /// Фильтр позднее даты - /// Фильтр свойств набора - /// - /// - /// - [HttpGet("{discriminatorId}")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task>> Get([FromRoute] Guid discriminatorId, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token) - { - var result = await timestampedValuesService.Get(discriminatorId, timestampBegin, columnNames, skip, take, token); - - return result.Any() ? Ok(result) : NoContent(); - } - - /// - /// Получение данных с фильтрацией для нескольких систем. Значение фильтра null - отключен - /// - /// Набор дискриминаторов (идентификаторов) + /// Набор дискриминаторов /// Фильтр позднее даты /// Фильтр свойств набора /// diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index 1a2ac57..e7d7be6 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -19,22 +19,6 @@ public interface ITimestampedValuesClient : IDisposable /// Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token); - /// - /// Получить данные с фильтрацией. Значение фильтра null - отключен - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр позднее даты - /// Фильтр свойств набора - /// - /// - /// - Task> Get(Guid discriminatorId, - DateTimeOffset? timestampBegin, - IEnumerable? columnNames, - int skip, - int take, - CancellationToken token); - /// /// Получить данные с фильтрацией для нескольких систем. Значение фильтра null - отключен /// @@ -99,19 +83,6 @@ public interface ITimestampedValuesClient : IDisposable /// Task GetDatesRange(Guid discriminatorId, CancellationToken token); - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); - /// /// /// diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs index e8457ad..7c56f12 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs @@ -17,17 +17,6 @@ public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable [Post($"{baseUrl}/{{discriminatorId}}")] Task> AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token); - /// - /// Получение данных с фильтрацией для нескольких систем - /// - [Get($"{baseUrl}/{{discriminatorId}}")] - Task>> Get(Guid discriminatorId, - DateTimeOffset? timestampBegin, - [Query(CollectionFormat.Multi)] IEnumerable? columnNames, - int skip, - int take, - CancellationToken token); - /// /// Получение данных с фильтрацией /// diff --git a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs index e5b35a3..4c77b20 100644 --- a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs @@ -31,14 +31,6 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result; } - /// - public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.Get(discriminatorId, geTimestamp, columnNames, skip, take, token), token); - return result!; - } - /// public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { @@ -101,15 +93,6 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient return result; } - /// - public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var data = await Get(idDiscriminator, geTimestamp, columnNames, skip, take, token); - var mapper = GetMapper(idDiscriminator); - - return data.Select(mapper.DeserializeTimeStampedData); - } - /// public async Task> GetLast(Guid idDiscriminator, int take, CancellationToken token) { diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs index 10dbd76..3131ec5 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs @@ -38,67 +38,67 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest await AddRange(discriminatorId); } + //[Fact] + //public async Task Get_returns_success() + //{ + // //arrange + // Cleanup(); + + // var discriminatorId = Guid.NewGuid(); + // discriminatorIds.Append(discriminatorId); + + // //act + // var response = await timestampedValuesClient.Get(discriminatorId, null, null, 0, 1, CancellationToken.None); + + // //assert + // Assert.Null(response); + //} + + //[Fact] + + //public async Task Get_AfterSave_returns_success() + //{ + // //arrange + // Cleanup(); + + // var discriminatorId = Guid.NewGuid(); + // discriminatorIds.Append(discriminatorId); + + // var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); + // var columnNames = new List() { "A", "C" }; + // var skip = 5; + // var take = 5; + + // var dtos = await AddRange(discriminatorId); + + // //act + // var response = await timestampedValuesClient.Get(discriminatorId, timestampBegin, columnNames, skip, take, CancellationToken.None); + + // //assert + // Assert.NotNull(response); + // Assert.NotEmpty(response); + + // var actualCount = response.Count(); + // Assert.Equal(take, actualCount); + + // var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList(); + // Assert.Equal(columnNames, actualColumnNames); + + // var expectedValueKind = JsonValueKind.Number; + // var actualValueKind = ((JsonElement) response.First().Values["A"]).ValueKind; + // Assert.Equal(expectedValueKind, actualValueKind); + + // expectedValueKind = JsonValueKind.String; + // actualValueKind = ((JsonElement)response.First().Values["C"]).ValueKind; + // Assert.Equal(expectedValueKind, actualValueKind); + //} + [Fact] public async Task Get_returns_success() { //arrange Cleanup(); - var discriminatorId = Guid.NewGuid(); - discriminatorIds.Append(discriminatorId); - - //act - var response = await timestampedValuesClient.Get(discriminatorId, null, null, 0, 1, CancellationToken.None); - - //assert - Assert.Null(response); - } - - [Fact] - - public async Task Get_AfterSave_returns_success() - { - //arrange - Cleanup(); - - var discriminatorId = Guid.NewGuid(); - discriminatorIds.Append(discriminatorId); - - var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); - var columnNames = new List() { "A", "C" }; - var skip = 5; - var take = 5; - - var dtos = await AddRange(discriminatorId); - - //act - var response = await timestampedValuesClient.Get(discriminatorId, timestampBegin, columnNames, skip, take, CancellationToken.None); - - //assert - Assert.NotNull(response); - Assert.NotEmpty(response); - - var actualCount = response.Count(); - Assert.Equal(take, actualCount); - - var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList(); - Assert.Equal(columnNames, actualColumnNames); - - var expectedValueKind = JsonValueKind.Number; - var actualValueKind = ((JsonElement) response.First().Values["A"]).ValueKind; - Assert.Equal(expectedValueKind, actualValueKind); - - expectedValueKind = JsonValueKind.String; - actualValueKind = ((JsonElement)response.First().Values["C"]).ValueKind; - Assert.Equal(expectedValueKind, actualValueKind); - } - - [Fact] - public async Task GetWithManyDiscriminators_returns_success() - { - //arrange - Cleanup(); - var firstDiscriminatorId = Guid.NewGuid(); discriminatorIds.Append(firstDiscriminatorId); @@ -113,7 +113,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest } [Fact] - public async Task GetWithManyDiscriminators_AfterSave_returns_success() + public async Task Get_AfterSave_returns_success() { //arrange Cleanup(); diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index 1a3ea47..3f4e0a0 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -3,7 +3,6 @@ using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Repositories; using Microsoft.EntityFrameworkCore; -using System.Linq; namespace DD.Persistence.Repository.Repositories; public class TimestampedValuesRepository : ITimestampedValuesRepository @@ -38,37 +37,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } - public async virtual Task>> Get(Guid discriminatorId, - DateTimeOffset? timestampBegin, - IEnumerable? columnNames, - int skip, - int take, - CancellationToken token) - { - var query = GetQueryReadOnly() - .Where(entity => entity.DiscriminatorId == discriminatorId); - - // Фильтрация по дате - if (timestampBegin.HasValue) - { - query = ApplyGeTimestamp(query, timestampBegin.Value); - } - - query = query - .OrderBy(item => item.Timestamp) - .Skip(skip) - .Take(take); - var entities = await query.ToArrayAsync(token); - - var result = entities.Select(e => Tuple.Create( - e.Timestamp, - e.Values - )); - - return result; - } - - public async virtual Task>> Get(IEnumerable discriminatorIds, + public async virtual Task>> Get(IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, @@ -84,28 +53,28 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository query = ApplyGeTimestamp(query, timestampBegin.Value); } - query = query - .OrderBy(item => item.Timestamp) - .Skip(skip) - .Take(take); - var entities = await query.ToArrayAsync(token); + // Группировка отсортированных значений по DiscriminatorId + var groupQuery = query + .GroupBy(e => e.DiscriminatorId) + .Select(g => KeyValuePair.Create(g.Key, g.OrderBy(i => i.Timestamp).Skip(skip).Take(take))); + var entities = await groupQuery.ToArrayAsync(token); - var result = entities.Select(e => Tuple.Create( + var result = entities.ToDictionary(k => k.Key, v => v.Value.Select(e => ( e.Timestamp, e.Values - )); + ))); return result; } - public async virtual Task>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) + public async virtual Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token) { var query = GetQueryReadOnly() .OrderBy(e => e.Timestamp) .Take(takeCount); var entities = await query.ToArrayAsync(token); - var result = entities.Select(e => Tuple.Create( + var result = entities.Select(e => ( e.Timestamp, e.Values )); @@ -113,14 +82,14 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } - public async virtual Task>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) + public async virtual Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token) { var query = GetQueryReadOnly() .OrderByDescending(e => e.Timestamp) .Take(takeCount); var entities = await query.ToArrayAsync(token); - var result = entities.Select(e => Tuple.Create( + var result = entities.Select(e => ( e.Timestamp, e.Values )); @@ -129,7 +98,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository } // ToDo: прореживание должно осуществляться до материализации - public async virtual Task>> GetResampledData( + public async virtual Task> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, @@ -150,13 +119,13 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return result; } - public async virtual Task>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) + public async virtual Task> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token) { var query = GetQueryReadOnly() .Where(e => e.Timestamp > timestampBegin); var entities = await query.ToArrayAsync(token); - var result = entities.Select(e => Tuple.Create( + var result = entities.Select(e => ( e.Timestamp, e.Values )); diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index c4db6ef..180bfc7 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -28,14 +28,14 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// /// Получение данных с фильтрацией. Значение фильтра null - отключен /// - /// Дискриминатор (идентификатор) набора + /// Набор дискриминаторов (идентификаторов) /// Фильтр позднее даты /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// /// /// /// - Task>> Get(Guid idDiscriminator, + Task>> Get(IEnumerable idDiscriminators, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, @@ -49,7 +49,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// Количество /// /// - Task>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token); + Task> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token); /// /// Получение данных с конца @@ -58,5 +58,5 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// Количество /// /// - Task>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token); + Task> GetLast(Guid discriminatorId, int takeCount, CancellationToken token); } diff --git a/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs index 95bfee2..417956d 100644 --- a/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs @@ -15,7 +15,7 @@ public interface ISyncRepository // ToDo: исчерпывающая абстр /// дата начала /// /// - Task>> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); + Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); /// diff --git a/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs index 04b8e1a..9c7e604 100644 --- a/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs @@ -16,7 +16,7 @@ public interface ITimeSeriesBaseRepository // ToDo: исчерпывающая /// /// /// - Task>> GetResampledData( + Task> GetResampledData( Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, diff --git a/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs b/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs index 0d075d6..27c5f16 100644 --- a/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs +++ b/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs @@ -25,19 +25,6 @@ public interface ITimestampedValuesService /// Task Count(Guid discriminatorId, CancellationToken token); - /// - /// Получение данных с фильтрацией. Значение фильтра null - отключен - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр позднее даты - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора - /// - /// - /// - /// - Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, - IEnumerable? columnNames, int skip, int take, CancellationToken token); - /// /// Получение данных с фильтрацией для нескольких систем. Значение фильтра null - отключен /// diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs index 31bb6b6..304fbc4 100644 --- a/DD.Persistence/Services/TimestampedValuesService.cs +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -34,31 +34,12 @@ public class TimestampedValuesService : ITimestampedValuesService return result; } - /// - public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var result = await timestampedValuesRepository.Get(discriminatorId, geTimestamp, columnNames, skip, take, token); - - var dtos = await Materialize(discriminatorId, result, token); - - if (!columnNames.IsNullOrEmpty()) - { - dtos = ReduceSetColumnsByNames(dtos, columnNames!); - } - - return dtos; - } - /// public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token); - List dtos = []; - discriminatorIds.ForEach(async discriminatorId => { - var materializeDtos = await Materialize(discriminatorId, result, token); - dtos.AddRange(materializeDtos); - }); + var dtos = await Materialize(result, token); if (!columnNames.IsNullOrEmpty()) { @@ -73,7 +54,9 @@ public class TimestampedValuesService : ITimestampedValuesService { var result = await timestampedValuesRepository.GetFirst(discriminatorId, takeCount, token); - var dtos = await Materialize(discriminatorId, result, token); + var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) } + .ToDictionary(); + var dtos = await Materialize(resultToMaterialize, token); return dtos; } @@ -83,7 +66,9 @@ public class TimestampedValuesService : ITimestampedValuesService { var result = await timestampedValuesRepository.GetLast(discriminatorId, takeCount, token); - var dtos = await Materialize(discriminatorId, result, token); + var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) } + .ToDictionary(); + var dtos = await Materialize(resultToMaterialize, token); return dtos; } @@ -98,7 +83,9 @@ public class TimestampedValuesService : ITimestampedValuesService { var result = await timestampedValuesRepository.GetResampledData(discriminatorId, beginTimestamp, intervalSec, approxPointsCount, token); - var dtos = await Materialize(discriminatorId, result, token); + var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) } + .ToDictionary(); + var dtos = await Materialize(resultToMaterialize, token); return dtos; } @@ -107,8 +94,10 @@ public class TimestampedValuesService : ITimestampedValuesService public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token) { var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token); - - var dtos = await Materialize(discriminatorId, result, token); + + var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) } + .ToDictionary(); + var dtos = await Materialize(resultToMaterialize, token); return dtos; } @@ -132,35 +121,38 @@ public class TimestampedValuesService : ITimestampedValuesService /// /// Преобразовать результат запроса в набор dto /// - /// /// /// /// - private async Task> Materialize(Guid dataSchemeId, IEnumerable> queryResult, CancellationToken token) + private async Task> Materialize(IDictionary> queryResult, CancellationToken token) { - var dataScheme = await dataSchemeRepository.Get(dataSchemeId, token); - if (dataScheme is null) + IEnumerable result = []; + foreach (var keyValuePair in queryResult) { - return []; + var dataScheme = await dataSchemeRepository.Get(keyValuePair.Key, token); + if (dataScheme is null) + { + continue; + } + + foreach (var tuple in keyValuePair.Value) + { + var identity = dataScheme!.PropNames; + var indexedIdentity = identity + .Select((value, index) => new { index, value }); + + var dto = new TimestampedValuesDto() + { + Timestamp = tuple.Timestamp.ToUniversalTime(), + Values = indexedIdentity + .ToDictionary(x => x.value, x => tuple.Values[x.index]) + }; + + result = result.Append(dto); + } } - var dtos = queryResult.Select(entity => - { - var dto = new TimestampedValuesDto() - { - Timestamp = entity.Item1.ToUniversalTime() - }; - - var identity = dataScheme!.PropNames; - var indexedIdentity = identity - .Select((value, index) => new { index, value }); - dto.Values = indexedIdentity - .ToDictionary(x => x.value, x => entity.Item2[x.index]); - - return dto; - }); - - return dtos; + return result; } ///