From ce0ad703696843faa5f9a001263f57b0efd40cd1 Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Tue, 14 Jan 2025 17:56:59 +0500 Subject: [PATCH] =?UTF-8?q?A=D0=BA=D1=82=D1=83=D0=B0=D0=BB=D0=B8=D0=B7?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20=D1=84=D1=83=D0=BD?= =?UTF-8?q?=D0=BA=D1=86=D0=B8=D0=BE=D0=BD=D0=B0=D0=BB=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B5=20=D1=81=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=BD=D1=8B=D0=BC=D0=B8=20=D1=80=D1=8F?= =?UTF-8?q?=D0=B4=D0=B0=D0=BC=D0=B8=20=D0=BF=D0=BE=D0=B4=20=D0=B5=D0=B4?= =?UTF-8?q?=D0=B8=D0=BD=D1=83=D1=8E=20=D1=81=D1=83=D1=89=D0=BD=D0=BE=D1=81?= =?UTF-8?q?=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataSaubController.cs | 20 -- .../Controllers/TagBagController.cs | 76 ------- .../Controllers/TimestampedSetController.cs | 69 +++--- DD.Persistence.App/appsettings.Tests.json | 2 +- .../Interfaces/ITimestampedSetClient.cs | 6 +- .../Refit/IRefitTimestampedSetClient.cs | 25 --- .../Refit/IRefitTimestampedValuesClient.cs | 28 +++ .../Clients/TimestampedSetClient.cs | 10 +- DD.Persistence.Client/DependencyInjection.cs | 1 - ...ner.cs => 20250114100429_Init.Designer.cs} | 110 ++-------- ...0062251_Init.cs => 20250114100429_Init.cs} | 47 +--- ...PersistencePostgresContextModelSnapshot.cs | 108 +--------- .../PersistenceDbContext.cs | 8 +- .../Controllers/DataSaubControllerTest.cs | 152 ++++++------- .../TimestampedSetControllerTest.cs | 42 ++-- .../DependencyInjection.cs | 4 +- .../Extensions/IEnumerableExtensions.cs | 17 ++ .../Repositories/TimestampedSetRepository.cs | 200 +++++++++--------- .../TimestampedValuesRepository.cs | 108 +++++++++- ...ampedSetDto.cs => TimestampedValuesDto.cs} | 8 +- .../Repositories/ITimestampedSetRepository.cs | 60 ------ .../ITimestampedValuesRepository.cs | 36 +++- .../ISyncRepository.cs | 11 +- .../ITimeSeriesBaseRepository.cs | 10 +- 24 files changed, 480 insertions(+), 678 deletions(-) delete mode 100644 DD.Persistence.API/Controllers/DataSaubController.cs delete mode 100644 DD.Persistence.API/Controllers/TagBagController.cs delete mode 100644 DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs create mode 100644 DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs rename DD.Persistence.Database.Postgres/Migrations/{20241220062251_Init.Designer.cs => 20250114100429_Init.Designer.cs} (66%) rename DD.Persistence.Database.Postgres/Migrations/{20241220062251_Init.cs => 20250114100429_Init.cs} (71%) create mode 100644 DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs rename DD.Persistence/Models/{TimestampedSetDto.cs => TimestampedValuesDto.cs} (62%) delete mode 100644 DD.Persistence/Repositories/ITimestampedSetRepository.cs rename DD.Persistence/{Repositories => RepositoriesAbstractions}/ISyncRepository.cs (60%) rename DD.Persistence/{Repositories => RepositoriesAbstractions}/ITimeSeriesBaseRepository.cs (65%) diff --git a/DD.Persistence.API/Controllers/DataSaubController.cs b/DD.Persistence.API/Controllers/DataSaubController.cs deleted file mode 100644 index df86844..0000000 --- a/DD.Persistence.API/Controllers/DataSaubController.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; -using DD.Persistence.Repositories; - -namespace DD.Persistence.API.Controllers; - -/// -/// Работа с временными данными -/// -[ApiController] -[Authorize] -[Route("api/[controller]")] -public class DataSaubController : TimeSeriesController -{ - public DataSaubController(ITimestampedValuesRepository timeSeriesDataRepository) : base(timeSeriesDataRepository) - { - - } -} diff --git a/DD.Persistence.API/Controllers/TagBagController.cs b/DD.Persistence.API/Controllers/TagBagController.cs deleted file mode 100644 index c7b8475..0000000 --- a/DD.Persistence.API/Controllers/TagBagController.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Repositories; -using DD.Persistence.ModelsAbstractions; - -namespace DD.Persistence.API.Controllers; - -[ApiController] -[Authorize] -[Route("api/[controller]")] -public class TimeSeriesController : ControllerBase, ITimeSeriesDataApi - where TDto : class, ITimestampAbstractDto, new() -{ - private readonly ITimestampedValuesRepository timeSeriesDataRepository; - - public TimeSeriesController(ITimestampedValuesRepository timeSeriesDataRepository) - { - this.timeSeriesDataRepository = timeSeriesDataRepository; - } - - /// - /// Получить список объектов, удовлетворяющий диапазону дат - /// - /// - /// - /// - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - public async Task Get(DateTimeOffset dateBegin, CancellationToken token) - { - var result = await timeSeriesDataRepository.GetGtDate(dateBegin, token); - return Ok(result); - } - - /// - /// Получить диапазон дат, для которых есть данные в репозитории - /// - /// - /// - [HttpGet("datesRange")] - public async Task GetDatesRange(CancellationToken token) - { - var result = await timeSeriesDataRepository.GetDatesRange(token); - return Ok(result); - } - - /// - /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат - /// - /// - /// - /// - /// - /// - [HttpGet("resampled")] - public async Task GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) - { - var result = await timeSeriesDataRepository.GetResampledData(dateBegin, intervalSec, approxPointsCount, token); - return Ok(result); - } - - /// - /// Добавить записи - /// - /// - /// - /// - [HttpPost] - public async Task AddRange(IEnumerable dtos, CancellationToken token) - { - var result = await timeSeriesDataRepository.AddRange(dtos, token); - return Ok(result); - } - - -} diff --git a/DD.Persistence.API/Controllers/TimestampedSetController.cs b/DD.Persistence.API/Controllers/TimestampedSetController.cs index c4514f5..878ee3d 100644 --- a/DD.Persistence.API/Controllers/TimestampedSetController.cs +++ b/DD.Persistence.API/Controllers/TimestampedSetController.cs @@ -1,9 +1,8 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using DD.Persistence.Models; -using DD.Persistence.Repositories; -using System.Net; +using DD.Persistence.Models; using DD.Persistence.Models.Common; +using DD.Persistence.Repositories; +using Microsoft.AspNetCore.Mvc; +using System.Net; namespace DD.Persistence.API.Controllers; @@ -12,13 +11,13 @@ namespace DD.Persistence.API.Controllers; /// Не оптимизировано под большие данные. /// [ApiController] -[Authorize] -[Route("api/[controller]/{idDiscriminator}")] +//[Authorize] +[Route("api/[controller]/{discriminatorId}")] public class TimestampedSetController : ControllerBase { - private readonly ITimestampedSetRepository repository; + private readonly ITimestampedValuesRepository repository; - public TimestampedSetController(ITimestampedSetRepository repository) + public TimestampedSetController(ITimestampedValuesRepository repository) { this.repository = repository; } @@ -27,22 +26,22 @@ public class TimestampedSetController : ControllerBase /// Записать новые данные /// Предполагается что данные с одним дискриминатором имеют одинаковую структуру /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// /// /// кол-во затронутых записей [HttpPost] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] - public async Task AddRange([FromRoute] Guid idDiscriminator, [FromBody] IEnumerable sets, CancellationToken token) + public async Task AddRange([FromRoute] Guid discriminatorId, [FromBody] IEnumerable sets, CancellationToken token) { - var result = await repository.AddRange(idDiscriminator, sets, token); + var result = await repository.AddRange(discriminatorId, sets, token); return Ok(result); } /// /// Получение данных с фильтрацией. Значение фильтра null - отключен /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// Фильтр позднее даты /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// @@ -50,56 +49,74 @@ public class TimestampedSetController : ControllerBase /// /// Фильтрованный набор данных с сортировкой по отметке времени [HttpGet] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, [FromQuery] IEnumerable? columnNames, int skip, int take, CancellationToken token) + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task Get([FromRoute] Guid discriminatorId, DateTimeOffset? geTimestamp, [FromQuery] IEnumerable? columnNames, int skip, int take, CancellationToken token) { - var result = await repository.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token); + var result = await repository.Get(discriminatorId, geTimestamp, columnNames, skip, take, token); return Ok(result); } /// /// Получить последние данные /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// /// /// Фильтрованный набор данных с сортировкой по отметке времени [HttpGet("last")] - [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] - public async Task GetLast(Guid idDiscriminator, [FromQuery] IEnumerable? columnNames, int take, CancellationToken token) + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + public async Task GetLast([FromRoute] Guid discriminatorId, [FromQuery] IEnumerable? columnNames, int take, CancellationToken token) { - var result = await repository.GetLast(idDiscriminator, columnNames, take, token); + var result = await repository.GetLast(discriminatorId, columnNames, take, token); return Ok(result); } /// /// Диапазон дат за которые есть данные /// - /// + /// /// /// Дата первой и последней записи [HttpGet("datesRange")] [ProducesResponseType(typeof(DatesRangeDto), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) + public async Task GetDatesRange([FromRoute] Guid discriminatorId, CancellationToken token) { - var result = await repository.GetDatesRange(idDiscriminator, token); + var result = await repository.GetDatesRange(discriminatorId, token); return Ok(result); } /// /// Количество записей по указанному набору в БД. Для пагинации. /// - /// Дискриминатор (идентификатор) набора + /// Дискриминатор (идентификатор) набора /// /// [HttpGet("count")] [ProducesResponseType(typeof(int), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task Count(Guid idDiscriminator, CancellationToken token) + public async Task Count([FromRoute] Guid discriminatorId, CancellationToken token) { - var result = await repository.Count(idDiscriminator, token); + var result = await repository.Count(discriminatorId, token); + return Ok(result); + } + + /// + /// Получить список объектов с прореживанием, удовлетворяющий диапазону дат + /// + /// + /// + /// + /// + /// + /// + [HttpGet("resampled")] + [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] + [ProducesResponseType((int)HttpStatusCode.NoContent)] + public async Task GetResampledData([FromRoute] Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) + { + var result = await repository.GetResampledData(discriminatorId, dateBegin, intervalSec, approxPointsCount, token); return Ok(result); } } diff --git a/DD.Persistence.App/appsettings.Tests.json b/DD.Persistence.App/appsettings.Tests.json index 72c43d3..9934757 100644 --- a/DD.Persistence.App/appsettings.Tests.json +++ b/DD.Persistence.App/appsettings.Tests.json @@ -1,6 +1,6 @@ { "DbConnection": { - "Host": "postgres", + "Host": "localhost", "Port": 5432, "Database": "persistence", "Username": "postgres", diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs index 5efd6e7..701beb9 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedSetClient.cs @@ -18,7 +18,7 @@ public interface ITimestampedSetClient : IDisposable /// /// /// - Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); + Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); /// /// Количество записей по указанному набору в БД. Для пагинации @@ -38,7 +38,7 @@ public interface ITimestampedSetClient : IDisposable /// /// /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); /// /// Диапазон дат за которые есть данные @@ -56,5 +56,5 @@ public interface ITimestampedSetClient : IDisposable /// /// /// - Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); + Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); } \ No newline at end of file diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs deleted file mode 100644 index 921e867..0000000 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedSetClient.cs +++ /dev/null @@ -1,25 +0,0 @@ -using DD.Persistence.Models; -using DD.Persistence.Models.Common; -using Refit; - -namespace DD.Persistence.Client.Clients.Interfaces.Refit; - -public interface IRefitTimestampedSetClient : IRefitClient, IDisposable -{ - private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}"; - - [Post(baseUrl)] - Task> AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); - - [Get(baseUrl)] - Task>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); - - [Get($"{baseUrl}/last")] - Task>> GetLast(Guid idDiscriminator, [Query] IEnumerable? columnNames, int take, CancellationToken token); - - [Get($"{baseUrl}/count")] - Task> Count(Guid idDiscriminator, CancellationToken token); - - [Get($"{baseUrl}/datesRange")] - Task> GetDatesRange(Guid idDiscriminator, CancellationToken token); -} diff --git a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs new file mode 100644 index 0000000..bf7a386 --- /dev/null +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs @@ -0,0 +1,28 @@ +using DD.Persistence.Models; +using DD.Persistence.Models.Common; +using Refit; + +namespace DD.Persistence.Client.Clients.Interfaces.Refit; + +public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable +{ + private const string baseUrl = "/api/TimestampedSet/{idDiscriminator}"; + + [Post(baseUrl)] + Task> AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); + + [Get(baseUrl)] + Task>> Get(Guid idDiscriminator, [Query] DateTimeOffset? geTimestamp, [Query] IEnumerable? columnNames, int skip, int take, CancellationToken token); + + [Get($"{baseUrl}/last")] + Task>> GetLast(Guid discriminatorId, [Query] IEnumerable? columnNames, int take, CancellationToken token); + + [Get($"{baseUrl}/count")] + Task> Count(Guid discriminatorId, CancellationToken token); + + [Get($"{baseUrl}/datesRange")] + Task> GetDatesRange(Guid discriminatorId, CancellationToken token); + + [Get($"{baseUrl}/resampled")] + Task>> GetResampledData(DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default); +} diff --git a/DD.Persistence.Client/Clients/TimestampedSetClient.cs b/DD.Persistence.Client/Clients/TimestampedSetClient.cs index d9f71dd..14fdee9 100644 --- a/DD.Persistence.Client/Clients/TimestampedSetClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedSetClient.cs @@ -8,14 +8,14 @@ using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients; public class TimestampedSetClient : BaseClient, ITimestampedSetClient { - private readonly IRefitTimestampedSetClient refitTimestampedSetClient; + private readonly IRefitTimestampedValuesClient refitTimestampedSetClient; - public TimestampedSetClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) + public TimestampedSetClient(IRefitClientFactory refitTimestampedSetClientFactory, ILogger logger) : base(logger) { this.refitTimestampedSetClient = refitTimestampedSetClientFactory.Create(); } - public async Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) + public async Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) { var result = await ExecutePostResponse( async () => await refitTimestampedSetClient.AddRange(idDiscriminator, sets, token), token); @@ -23,7 +23,7 @@ public class TimestampedSetClient : BaseClient, ITimestampedSetClient return result; } - public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var result = await ExecuteGetResponse( async () => await refitTimestampedSetClient.Get(idDiscriminator, geTimestamp, columnNames, skip, take, token), token); @@ -31,7 +31,7 @@ public class TimestampedSetClient : BaseClient, ITimestampedSetClient return result!; } - public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) + public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) { var result = await ExecuteGetResponse( async () => await refitTimestampedSetClient.GetLast(idDiscriminator, columnNames, take, token), token); diff --git a/DD.Persistence.Client/DependencyInjection.cs b/DD.Persistence.Client/DependencyInjection.cs index eced892..43a8733 100644 --- a/DD.Persistence.Client/DependencyInjection.cs +++ b/DD.Persistence.Client/DependencyInjection.cs @@ -22,7 +22,6 @@ public static class DependencyInjection services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient, TimeSeriesClient>(); services.AddTransient(); services.AddTransient(); return services; diff --git a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs similarity index 66% rename from DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.Designer.cs rename to DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs index 02a25ce..8d0173d 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.Designer.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DD.Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistencePostgresContext))] - [Migration("20241220062251_Init")] + [Migration("20250114100429_Init")] partial class Init { /// @@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("ProductVersion", "9.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -105,27 +105,24 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("TechMessage"); }); - modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b => + modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => { - b.Property("IdDiscriminator") + b.Property("DiscriminatorId") .HasColumnType("uuid") - .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + .HasComment("Дискриминатор системы"); b.Property("Timestamp") .HasColumnType("timestamp with time zone") - .HasComment("Отметка времени, строго в UTC"); + .HasComment("Временная отметка"); - b.Property("Set") + b.Property("Values") .IsRequired() .HasColumnType("jsonb") - .HasComment("Набор сохраняемых данных"); + .HasComment("Данные"); - b.HasKey("IdDiscriminator", "Timestamp"); + b.HasKey("DiscriminatorId", "Timestamp"); - b.ToTable("TimestampedSets", t => - { - t.HasComment("Общая таблица данных временных рядов"); - }); + b.ToTable("TimestampedSets"); }); modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => @@ -181,96 +178,13 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("ChangeLog"); }); - modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b => - { - b.Property("Date") - .HasColumnType("timestamp with time zone") - .HasColumnName("date"); - - b.Property("AxialLoad") - .HasColumnType("double precision") - .HasColumnName("axialLoad"); - - b.Property("BitDepth") - .HasColumnType("double precision") - .HasColumnName("bitDepth"); - - b.Property("BlockPosition") - .HasColumnType("double precision") - .HasColumnName("blockPosition"); - - b.Property("BlockSpeed") - .HasColumnType("double precision") - .HasColumnName("blockSpeed"); - - b.Property("Flow") - .HasColumnType("double precision") - .HasColumnName("flow"); - - b.Property("HookWeight") - .HasColumnType("double precision") - .HasColumnName("hookWeight"); - - b.Property("IdFeedRegulator") - .HasColumnType("integer") - .HasColumnName("idFeedRegulator"); - - b.Property("Mode") - .HasColumnType("integer") - .HasColumnName("mode"); - - b.Property("Mse") - .HasColumnType("double precision") - .HasColumnName("mse"); - - b.Property("MseState") - .HasColumnType("smallint") - .HasColumnName("mseState"); - - b.Property("Pressure") - .HasColumnType("double precision") - .HasColumnName("pressure"); - - b.Property("Pump0Flow") - .HasColumnType("double precision") - .HasColumnName("pump0Flow"); - - b.Property("Pump1Flow") - .HasColumnType("double precision") - .HasColumnName("pump1Flow"); - - b.Property("Pump2Flow") - .HasColumnType("double precision") - .HasColumnName("pump2Flow"); - - b.Property("RotorSpeed") - .HasColumnType("double precision") - .HasColumnName("rotorSpeed"); - - b.Property("RotorTorque") - .HasColumnType("double precision") - .HasColumnName("rotorTorque"); - - b.Property("User") - .HasColumnType("text") - .HasColumnName("user"); - - b.Property("WellDepth") - .HasColumnType("double precision") - .HasColumnName("wellDepth"); - - b.HasKey("Date"); - - b.ToTable("DataSaub"); - }); - modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b => { b.Property("Key") .HasColumnType("uuid") .HasComment("Ключ"); - b.Property("Created") + b.Property("Timestamp") .HasColumnType("timestamp with time zone") .HasComment("Дата создания уставки"); @@ -283,7 +197,7 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("jsonb") .HasComment("Значение уставки"); - b.HasKey("Key", "Created"); + b.HasKey("Key", "Timestamp"); b.ToTable("Setpoint"); }); diff --git a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs similarity index 71% rename from DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.cs rename to DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs index 03a8c43..999a231 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20241220062251_Init.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250114100429_Init.cs @@ -32,35 +32,6 @@ namespace DD.Persistence.Database.Postgres.Migrations table.PrimaryKey("PK_ChangeLog", x => x.Id); }); - migrationBuilder.CreateTable( - name: "DataSaub", - columns: table => new - { - date = table.Column(type: "timestamp with time zone", nullable: false), - mode = table.Column(type: "integer", nullable: true), - user = table.Column(type: "text", nullable: true), - wellDepth = table.Column(type: "double precision", nullable: true), - bitDepth = table.Column(type: "double precision", nullable: true), - blockPosition = table.Column(type: "double precision", nullable: true), - blockSpeed = table.Column(type: "double precision", nullable: true), - pressure = table.Column(type: "double precision", nullable: true), - axialLoad = table.Column(type: "double precision", nullable: true), - hookWeight = table.Column(type: "double precision", nullable: true), - rotorTorque = table.Column(type: "double precision", nullable: true), - rotorSpeed = table.Column(type: "double precision", nullable: true), - flow = table.Column(type: "double precision", nullable: true), - mseState = table.Column(type: "smallint", nullable: false), - idFeedRegulator = table.Column(type: "integer", nullable: false), - mse = table.Column(type: "double precision", nullable: true), - pump0Flow = table.Column(type: "double precision", nullable: true), - pump1Flow = table.Column(type: "double precision", nullable: true), - pump2Flow = table.Column(type: "double precision", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_DataSaub", x => x.date); - }); - migrationBuilder.CreateTable( name: "DataSourceSystem", columns: table => new @@ -93,28 +64,27 @@ namespace DD.Persistence.Database.Postgres.Migrations columns: table => new { Key = table.Column(type: "uuid", nullable: false, comment: "Ключ"), - Created = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"), + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания уставки"), Value = table.Column(type: "jsonb", nullable: false, comment: "Значение уставки"), IdUser = table.Column(type: "uuid", nullable: false, comment: "Id автора последнего изменения") }, constraints: table => { - table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Created }); + table.PrimaryKey("PK_Setpoint", x => new { x.Key, x.Timestamp }); }); migrationBuilder.CreateTable( name: "TimestampedSets", columns: table => new { - IdDiscriminator = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор ссылка на тип сохраняемых данных"), - Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Отметка времени, строго в UTC"), - Set = table.Column(type: "jsonb", nullable: false, comment: "Набор сохраняемых данных") + Timestamp = table.Column(type: "timestamp with time zone", nullable: false, comment: "Временная отметка"), + DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор системы"), + Values = table.Column(type: "jsonb", nullable: false, comment: "Данные") }, constraints: table => { - table.PrimaryKey("PK_TimestampedSets", x => new { x.IdDiscriminator, x.Timestamp }); - }, - comment: "Общая таблица данных временных рядов"); + table.PrimaryKey("PK_TimestampedSets", x => new { x.DiscriminatorId, x.Timestamp }); + }); migrationBuilder.CreateTable( name: "TechMessage", @@ -150,9 +120,6 @@ namespace DD.Persistence.Database.Postgres.Migrations migrationBuilder.DropTable( name: "ChangeLog"); - migrationBuilder.DropTable( - name: "DataSaub"); - migrationBuilder.DropTable( name: "ParameterData"); diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs index 4c66e90..6e10f12 100644 --- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs +++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.10") + .HasAnnotation("ProductVersion", "9.0.0") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -102,27 +102,24 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("TechMessage"); }); - modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedSet", b => + modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b => { - b.Property("IdDiscriminator") + b.Property("DiscriminatorId") .HasColumnType("uuid") - .HasComment("Дискриминатор ссылка на тип сохраняемых данных"); + .HasComment("Дискриминатор системы"); b.Property("Timestamp") .HasColumnType("timestamp with time zone") - .HasComment("Отметка времени, строго в UTC"); + .HasComment("Временная отметка"); - b.Property("Set") + b.Property("Values") .IsRequired() .HasColumnType("jsonb") - .HasComment("Набор сохраняемых данных"); + .HasComment("Данные"); - b.HasKey("IdDiscriminator", "Timestamp"); + b.HasKey("DiscriminatorId", "Timestamp"); - b.ToTable("TimestampedSets", t => - { - t.HasComment("Общая таблица данных временных рядов"); - }); + b.ToTable("TimestampedSets"); }); modelBuilder.Entity("DD.Persistence.Database.Model.ChangeLog", b => @@ -178,96 +175,13 @@ namespace DD.Persistence.Database.Postgres.Migrations b.ToTable("ChangeLog"); }); - modelBuilder.Entity("DD.Persistence.Database.Model.DataSaub", b => - { - b.Property("Date") - .HasColumnType("timestamp with time zone") - .HasColumnName("date"); - - b.Property("AxialLoad") - .HasColumnType("double precision") - .HasColumnName("axialLoad"); - - b.Property("BitDepth") - .HasColumnType("double precision") - .HasColumnName("bitDepth"); - - b.Property("BlockPosition") - .HasColumnType("double precision") - .HasColumnName("blockPosition"); - - b.Property("BlockSpeed") - .HasColumnType("double precision") - .HasColumnName("blockSpeed"); - - b.Property("Flow") - .HasColumnType("double precision") - .HasColumnName("flow"); - - b.Property("HookWeight") - .HasColumnType("double precision") - .HasColumnName("hookWeight"); - - b.Property("IdFeedRegulator") - .HasColumnType("integer") - .HasColumnName("idFeedRegulator"); - - b.Property("Mode") - .HasColumnType("integer") - .HasColumnName("mode"); - - b.Property("Mse") - .HasColumnType("double precision") - .HasColumnName("mse"); - - b.Property("MseState") - .HasColumnType("smallint") - .HasColumnName("mseState"); - - b.Property("Pressure") - .HasColumnType("double precision") - .HasColumnName("pressure"); - - b.Property("Pump0Flow") - .HasColumnType("double precision") - .HasColumnName("pump0Flow"); - - b.Property("Pump1Flow") - .HasColumnType("double precision") - .HasColumnName("pump1Flow"); - - b.Property("Pump2Flow") - .HasColumnType("double precision") - .HasColumnName("pump2Flow"); - - b.Property("RotorSpeed") - .HasColumnType("double precision") - .HasColumnName("rotorSpeed"); - - b.Property("RotorTorque") - .HasColumnType("double precision") - .HasColumnName("rotorTorque"); - - b.Property("User") - .HasColumnType("text") - .HasColumnName("user"); - - b.Property("WellDepth") - .HasColumnType("double precision") - .HasColumnName("wellDepth"); - - b.HasKey("Date"); - - b.ToTable("DataSaub"); - }); - modelBuilder.Entity("DD.Persistence.Database.Model.Setpoint", b => { b.Property("Key") .HasColumnType("uuid") .HasComment("Ключ"); - b.Property("Created") + b.Property("Timestamp") .HasColumnType("timestamp with time zone") .HasComment("Дата создания уставки"); @@ -280,7 +194,7 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("jsonb") .HasComment("Значение уставки"); - b.HasKey("Key", "Created"); + b.HasKey("Key", "Timestamp"); b.ToTable("Setpoint"); }); diff --git a/DD.Persistence.Database/PersistenceDbContext.cs b/DD.Persistence.Database/PersistenceDbContext.cs index a0587cd..0cddcba 100644 --- a/DD.Persistence.Database/PersistenceDbContext.cs +++ b/DD.Persistence.Database/PersistenceDbContext.cs @@ -9,11 +9,9 @@ namespace DD.Persistence.Database; /// public class PersistenceDbContext : DbContext { - public DbSet DataSaub => Set(); - public DbSet Setpoint => Set(); - public DbSet TimestampedSets => Set(); + public DbSet TimestampedSets => Set(); public DbSet ChangeLog => Set(); @@ -31,8 +29,8 @@ public class PersistenceDbContext : DbContext protected override void OnModelCreating(ModelBuilder modelBuilder) { - modelBuilder.Entity() - .Property(e => e.Set) + modelBuilder.Entity() + .Property(e => e.Values) .HasJsonConversion(); modelBuilder.Entity() diff --git a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs index 3429651..bb82675 100644 --- a/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/DataSaubControllerTest.cs @@ -1,85 +1,85 @@ -using DD.Persistence.Database.Model; -using DD.Persistence.Models; -using Xunit; +//using DD.Persistence.Database.Model; +//using DD.Persistence.Models; +//using Xunit; -namespace DD.Persistence.IntegrationTests.Controllers; -public class DataSaubControllerTest : TimeSeriesBaseControllerTest -{ - private readonly DataSaubDto dto = new() - { - AxialLoad = 1, - BitDepth = 2, - BlockPosition = 3, - BlockSpeed = 4, - Date = DateTimeOffset.UtcNow, - Flow = 5, - HookWeight = 6, - IdFeedRegulator = 8, - Mode = 9, - Mse = 10, - MseState = 11, - Pressure = 12, - Pump0Flow = 13, - Pump1Flow = 14, - Pump2Flow = 15, - RotorSpeed = 16, - RotorTorque = 17, - User = string.Empty, - WellDepth = 18, - }; +//namespace DD.Persistence.IntegrationTests.Controllers; +//public class DataSaubControllerTest : TimeSeriesBaseControllerTest +//{ +// private readonly DataSaubDto dto = new() +// { +// AxialLoad = 1, +// BitDepth = 2, +// BlockPosition = 3, +// BlockSpeed = 4, +// Date = DateTimeOffset.UtcNow, +// Flow = 5, +// HookWeight = 6, +// IdFeedRegulator = 8, +// Mode = 9, +// Mse = 10, +// MseState = 11, +// Pressure = 12, +// Pump0Flow = 13, +// Pump1Flow = 14, +// Pump2Flow = 15, +// RotorSpeed = 16, +// RotorTorque = 17, +// User = string.Empty, +// WellDepth = 18, +// }; - private readonly DataSaub entity = new() - { - AxialLoad = 1, - BitDepth = 2, - BlockPosition = 3, - BlockSpeed = 4, - Timestamp = DateTimeOffset.UtcNow, - Flow = 5, - HookWeight = 6, - IdFeedRegulator = 8, - Mode = 9, - Mse = 10, - MseState = 11, - Pressure = 12, - Pump0Flow = 13, - Pump1Flow = 14, - Pump2Flow = 15, - RotorSpeed = 16, - RotorTorque = 17, - User = string.Empty, - WellDepth = 18, - }; +// private readonly DataSaub entity = new() +// { +// AxialLoad = 1, +// BitDepth = 2, +// BlockPosition = 3, +// BlockSpeed = 4, +// Timestamp = DateTimeOffset.UtcNow, +// Flow = 5, +// HookWeight = 6, +// IdFeedRegulator = 8, +// Mode = 9, +// Mse = 10, +// MseState = 11, +// Pressure = 12, +// Pump0Flow = 13, +// Pump1Flow = 14, +// Pump2Flow = 15, +// RotorSpeed = 16, +// RotorTorque = 17, +// User = string.Empty, +// WellDepth = 18, +// }; - public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory) - { - } +// public DataSaubControllerTest(WebAppFactoryFixture factory) : base(factory) +// { +// } - [Fact] - public async Task InsertRange_returns_success() - { - await InsertRangeSuccess(dto); - } +// [Fact] +// public async Task InsertRange_returns_success() +// { +// await InsertRangeSuccess(dto); +// } - [Fact] - public async Task Get_returns_success() - { - var beginDate = DateTimeOffset.UtcNow.AddDays(-1); - var endDate = DateTimeOffset.UtcNow; - await GetSuccess(beginDate, endDate, entity); - } +// [Fact] +// public async Task Get_returns_success() +// { +// var beginDate = DateTimeOffset.UtcNow.AddDays(-1); +// var endDate = DateTimeOffset.UtcNow; +// await GetSuccess(beginDate, endDate, entity); +// } - [Fact] - public async Task GetDatesRange_returns_success() - { - await GetDatesRangeSuccess(entity); - } +// [Fact] +// public async Task GetDatesRange_returns_success() +// { +// await GetDatesRangeSuccess(entity); +// } - [Fact] - public async Task GetResampledData_returns_success() - { - await GetResampledDataSuccess(entity); - } -} +// [Fact] +// public async Task GetResampledData_returns_success() +// { +// await GetResampledDataSuccess(entity); +// } +//} diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs index 1e84e9d..c929230 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedSetControllerTest.cs @@ -15,7 +15,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest public TimestampedSetControllerTest(WebAppFactoryFixture factory) : base(factory) { var refitClientFactory = scope.ServiceProvider - .GetRequiredService>(); + .GetRequiredService>(); var logger = scope.ServiceProvider.GetRequiredService>(); client = scope.ServiceProvider @@ -27,7 +27,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest { // arrange Guid idDiscriminator = Guid.NewGuid(); - IEnumerable testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(10, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); // act var response = await client.AddRange(idDiscriminator, testSets, CancellationToken.None); @@ -42,7 +42,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); // act @@ -59,7 +59,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); string[] props = ["A"]; @@ -71,9 +71,9 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Assert.Equal(count, response.Count()); foreach (var item in response) { - Assert.Single(item.Set); - var kv = item.Set.First(); - Assert.Equal("A", kv.Key); + Assert.Single(item.Values); + var kv = item.Values.First(); + Assert.Equal("A", ((KeyValuePair) kv).Key); } } @@ -85,7 +85,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest int count = 10; var dateMin = DateTimeOffset.Now; var dateMax = DateTimeOffset.Now.AddSeconds(count); - IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); var insertResponse = await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var tail = testSets.OrderBy(t => t.Timestamp).Skip(count / 2).Take(int.MaxValue); var geDate = tail.First().Timestamp; @@ -108,7 +108,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var expectedCount = count / 2; @@ -128,7 +128,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Guid idDiscriminator = Guid.NewGuid(); var expectedCount = 1; int count = 10 + expectedCount; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); // act @@ -145,7 +145,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 10; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var expectedCount = 8; @@ -165,7 +165,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest int count = 10; var dateMin = DateTimeOffset.Now; var dateMax = DateTimeOffset.Now.AddSeconds(count - 1); - IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, dateMin.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); var tolerance = TimeSpan.FromSeconds(1); @@ -184,7 +184,7 @@ public class TimestampedSetControllerTest : BaseIntegrationTest // arrange Guid idDiscriminator = Guid.NewGuid(); int count = 144; - IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); + IEnumerable testSets = Generate(count, DateTimeOffset.Now.ToOffset(TimeSpan.FromHours(7))); await client.AddRange(idDiscriminator, testSets, CancellationToken.None); // act @@ -194,18 +194,20 @@ public class TimestampedSetControllerTest : BaseIntegrationTest Assert.Equal(count, response); } - private static IEnumerable Generate(int n, DateTimeOffset from) + private static IEnumerable Generate(int n, DateTimeOffset from) { for (int i = 0; i < n; i++) - yield return new TimestampedSetDto - ( - from.AddSeconds(i), - new Dictionary{ + yield return new TimestampedValuesDto() + { + Timestamp = from.AddSeconds(i), + Values = new Dictionary { {"A", i }, {"B", i * 1.1 }, {"C", $"Any{i}" }, - {"D", DateTimeOffset.Now}, + {"D", DateTimeOffset.Now} } - ); + .Select(e => (object) e) + .ToArray() + }; } } diff --git a/DD.Persistence.Repository/DependencyInjection.cs b/DD.Persistence.Repository/DependencyInjection.cs index be9ed55..492c502 100644 --- a/DD.Persistence.Repository/DependencyInjection.cs +++ b/DD.Persistence.Repository/DependencyInjection.cs @@ -36,11 +36,9 @@ public static class DependencyInjection MapsterSetup(); - services.AddTransient, TagBagDataRepository>(); services.AddTransient(); - //services.AddTransient, TimeSeriesDataCachedRepository>(); services.AddTransient(); - services.AddTransient(); + services.AddTransient, TimestampedValuesRepository>(); services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs b/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs new file mode 100644 index 0000000..270107f --- /dev/null +++ b/DD.Persistence.Repository/Extensions/IEnumerableExtensions.cs @@ -0,0 +1,17 @@ +namespace DD.Persistence.Repository.Extensions; + +public static class IEnumerableExtensions +{ + public static void ForEach(this IEnumerable source, Action action) + { + if (source == null) + throw new ArgumentNullException(nameof(source)); + if (action == null) + throw new ArgumentNullException(nameof(action)); + + foreach (var item in source) + { + action(item); + } + } +} diff --git a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs index c265d4d..73225be 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedSetRepository.cs @@ -1,122 +1,122 @@ -using Microsoft.EntityFrameworkCore; -using DD.Persistence.Database.Entity; -using DD.Persistence.Models; -using DD.Persistence.Repositories; -using DD.Persistence.Models.Common; +//using Microsoft.EntityFrameworkCore; +//using DD.Persistence.Database.Entity; +//using DD.Persistence.Models; +//using DD.Persistence.Repositories; +//using DD.Persistence.Models.Common; -namespace DD.Persistence.Repository.Repositories; +//namespace DD.Persistence.Repository.Repositories; -/// -/// Репозиторий для хранения разных наборов данных временных рядов. -/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. -/// idDiscriminator формируют клиенты и только им известно что они обозначают. -/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. -/// -public class TimestampedSetRepository : ITimestampedSetRepository -{ - private readonly DbContext db; +///// +///// Репозиторий для хранения разных наборов данных временных рядов. +///// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. +///// idDiscriminator формируют клиенты и только им известно что они обозначают. +///// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. +///// +//public class TimestampedSetRepository : ITimestampedSetRepository +//{ +// private readonly DbContext db; - public TimestampedSetRepository(DbContext db) - { - this.db = db; - } +// public TimestampedSetRepository(DbContext db) +// { +// this.db = db; +// } - public Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) - { - var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set)); - var dbSet = db.Set(); - dbSet.AddRange(entities); - return db.SaveChangesAsync(token); - } +// public Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token) +// { +// var entities = sets.Select(set => new TimestampedSet(idDiscriminator, set.Timestamp.ToUniversalTime(), set.Set)); +// var dbSet = db.Set(); +// dbSet.AddRange(entities); +// return db.SaveChangesAsync(token); +// } - public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) - { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); +// public async Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) +// { +// var dbSet = db.Set(); +// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - if (geTimestamp.HasValue) - query = ApplyGeTimestamp(query, geTimestamp.Value); +// if (geTimestamp.HasValue) +// query = ApplyGeTimestamp(query, geTimestamp.Value); - query = query - .OrderBy(item => item.Timestamp) - .Skip(skip) - .Take(take); +// query = query +// .OrderBy(item => item.Timestamp) +// .Skip(skip) +// .Take(take); - var data = await Materialize(query, token); +// var data = await Materialize(query, token); - if (columnNames is not null && columnNames.Any()) - data = ReduceSetColumnsByNames(data, columnNames); +// if (columnNames is not null && columnNames.Any()) +// data = ReduceSetColumnsByNames(data, columnNames); - return data; - } +// return data; +// } - public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) - { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); +// public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) +// { +// var dbSet = db.Set(); +// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - query = query.OrderByDescending(entity => entity.Timestamp) - .Take(take) - .OrderBy(entity => entity.Timestamp); +// query = query.OrderByDescending(entity => entity.Timestamp) +// .Take(take) +// .OrderBy(entity => entity.Timestamp); - var data = await Materialize(query, token); +// var data = await Materialize(query, token); - if (columnNames is not null && columnNames.Any()) - data = ReduceSetColumnsByNames(data, columnNames); +// if (columnNames is not null && columnNames.Any()) +// data = ReduceSetColumnsByNames(data, columnNames); - return data; - } +// return data; +// } - public Task Count(Guid idDiscriminator, CancellationToken token) - { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); - return query.CountAsync(token); - } +// public Task Count(Guid idDiscriminator, CancellationToken token) +// { +// var dbSet = db.Set(); +// var query = dbSet.Where(entity => entity.IdDiscriminator == idDiscriminator); +// return query.CountAsync(token); +// } - public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) - { - var query = db.Set() - .GroupBy(entity => entity.IdDiscriminator) - .Select(group => new - { - Min = group.Min(entity => entity.Timestamp), - Max = group.Max(entity => entity.Timestamp), - }); +// public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) +// { +// var query = db.Set() +// .GroupBy(entity => entity.IdDiscriminator) +// .Select(group => new +// { +// Min = group.Min(entity => entity.Timestamp), +// Max = group.Max(entity => entity.Timestamp), +// }); - var item = await query.FirstOrDefaultAsync(token); - if (item is null) - return null; +// var item = await query.FirstOrDefaultAsync(token); +// if (item is null) +// return null; - return new DatesRangeDto - { - From = item.Min, - To = item.Max, - }; - } +// return new DatesRangeDto +// { +// From = item.Min, +// To = item.Max, +// }; +// } - private static async Task> Materialize(IQueryable query, CancellationToken token) - { - var dtoQuery = query.Select(entity => new TimestampedSetDto(entity.Timestamp, entity.Set)); - var dtos = await dtoQuery.ToArrayAsync(token); - return dtos; - } +// private static async Task> Materialize(IQueryable query, CancellationToken token) +// { +// var dtoQuery = query.Select(entity => new TimestampedSetDto(entity.Timestamp, entity.Set)); +// var dtos = await dtoQuery.ToArrayAsync(token); +// return dtos; +// } - private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) - { - var geTimestampUtc = geTimestamp.ToUniversalTime(); - return query.Where(entity => entity.Timestamp >= geTimestampUtc); - } +// private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) +// { +// var geTimestampUtc = geTimestamp.ToUniversalTime(); +// return query.Where(entity => entity.Timestamp >= geTimestampUtc); +// } - private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) - { - var newQuery = query - .Select(entity => new TimestampedSetDto( - entity.Timestamp, - entity.Set - .Where(prop => columnNames.Contains(prop.Key)) - .ToDictionary(prop => prop.Key, prop => prop.Value) - )); - return newQuery; - } -} +// private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) +// { +// var newQuery = query +// .Select(entity => new TimestampedSetDto( +// entity.Timestamp, +// entity.Set +// .Where(prop => columnNames.Contains(prop.Key)) +// .ToDictionary(prop => prop.Key, prop => prop.Value) +// )); +// return newQuery; +// } +//} diff --git a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs index d055594..9ab1877 100644 --- a/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Repository/Repositories/TimestampedValuesRepository.cs @@ -1,9 +1,11 @@ using DD.Persistence.Database.Entity; +using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.ModelsAbstractions; using DD.Persistence.Repositories; using Mapster; using Microsoft.EntityFrameworkCore; +using System.Text.Json; namespace DD.Persistence.Repository.Repositories; public class TimestampedValuesRepository : ITimestampedValuesRepository @@ -18,20 +20,28 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository GetQueryReadOnly() => this.db.Set(); - public virtual async Task GetDatesRange(CancellationToken token) + public virtual async Task GetDatesRange(Guid discriminatorId, CancellationToken token) { - var query = GetQueryReadOnly(); - var minDate = await query.MinAsync(o => o.Timestamp, token); - var maxDate = await query.MaxAsync(o => o.Timestamp, token); + var query = GetQueryReadOnly() + .GroupBy(entity => entity.DiscriminatorId) + .Select(group => new + { + Min = group.Min(entity => entity.Timestamp), + Max = group.Max(entity => entity.Timestamp), + }); + + var item = await query.FirstOrDefaultAsync(token); + if (item is null) + return null; return new DatesRangeDto { - From = minDate, - To = maxDate + From = item.Min, + To = item.Max, }; } - public virtual async Task> GetGtDate(DateTimeOffset date, CancellationToken token) + public virtual async Task> GetGtDate(Guid discriminatorId, DateTimeOffset date, CancellationToken token) { var query = GetQueryReadOnly().Where(e => e.Timestamp > date); var entities = await query.ToArrayAsync(token); @@ -41,9 +51,13 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository AddRange(IEnumerable dtos, CancellationToken token) + public virtual async Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { - var entities = dtos.Select(d => d.Adapt()); + var entities = dtos + .Select(d => d.Adapt()) + .ToList(); + + entities.ForEach(d => d.DiscriminatorId = discriminatorId); await db.Set().AddRangeAsync(entities, token); var result = await db.SaveChangesAsync(token); @@ -78,12 +92,13 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository> GetResampledData( + Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024, CancellationToken token = default) { - var dtos = await GetGtDate(dateBegin, token); + var dtos = await GetGtDate(discriminatorId, dateBegin, token); var dateEnd = dateBegin.AddSeconds(intervalSec); dtos = dtos @@ -96,4 +111,77 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var dbSet = db.Set(); + var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + + if (geTimestamp.HasValue) + query = ApplyGeTimestamp(query, geTimestamp.Value); + + query = query + .OrderBy(item => item.Timestamp) + .Skip(skip) + .Take(take); + + var data = await Materialize(query, token); + + if (columnNames is not null && columnNames.Any()) + data = ReduceSetColumnsByNames(data, columnNames); + + return data; + } + + public async Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token) + { + var dbSet = db.Set(); + var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + + query = query.OrderByDescending(entity => entity.Timestamp) + .Take(take) + .OrderBy(entity => entity.Timestamp); + + var data = await Materialize(query, token); + + if (columnNames is not null && columnNames.Any()) + data = ReduceSetColumnsByNames(data, columnNames); + + return data; + } + + public Task Count(Guid idDiscriminator, CancellationToken token) + { + var dbSet = db.Set(); + var query = dbSet.Where(entity => entity.DiscriminatorId == idDiscriminator); + return query.CountAsync(token); + } + + private static async Task> Materialize(IQueryable query, CancellationToken token) + { + var dtoQuery = query.Select(entity => new TimestampedValuesDto() { Timestamp = entity.Timestamp, Values = entity.Values }); + var dtos = await dtoQuery.ToArrayAsync(token); + return dtos; + } + + private static IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset geTimestamp) + { + var geTimestampUtc = geTimestamp.ToUniversalTime(); + return query.Where(entity => entity.Timestamp >= geTimestampUtc); + } + + private static IEnumerable ReduceSetColumnsByNames(IEnumerable query, IEnumerable columnNames) + { + var newQuery = query + .Select(entity => new TimestampedValuesDto() + { + Timestamp = entity.Timestamp, + Values = entity.Values? + .Where(prop => columnNames.Contains( + JsonSerializer.Deserialize>(prop.ToString()!)? + .FirstOrDefault().Key + )).ToArray() + }); + return newQuery; + } } diff --git a/DD.Persistence/Models/TimestampedSetDto.cs b/DD.Persistence/Models/TimestampedValuesDto.cs similarity index 62% rename from DD.Persistence/Models/TimestampedSetDto.cs rename to DD.Persistence/Models/TimestampedValuesDto.cs index 20c5cbb..938970d 100644 --- a/DD.Persistence/Models/TimestampedSetDto.cs +++ b/DD.Persistence/Models/TimestampedValuesDto.cs @@ -1,9 +1,11 @@ -namespace DD.Persistence.Models; +using DD.Persistence.ModelsAbstractions; + +namespace DD.Persistence.Models; /// /// Набор данных с отметкой времени /// -public class TimestampedSetDto +public class TimestampedValuesDto : ITimestampAbstractDto { /// /// Временная отметка @@ -13,5 +15,5 @@ public class TimestampedSetDto /// /// Набор данных /// - public required object[] Set { get; set; } + public object[]? Values { get; set; } } diff --git a/DD.Persistence/Repositories/ITimestampedSetRepository.cs b/DD.Persistence/Repositories/ITimestampedSetRepository.cs deleted file mode 100644 index c448744..0000000 --- a/DD.Persistence/Repositories/ITimestampedSetRepository.cs +++ /dev/null @@ -1,60 +0,0 @@ -using DD.Persistence.Models; -using DD.Persistence.Models.Common; - -namespace DD.Persistence.Repositories; - -/// -/// Репозиторий для хранения разных наборов данных рядов. -/// idDiscriminator - идентифицирует конкретный набор данных, прим.: циклы измерения АСИБР, или отчет о DrillTest. -/// idDiscriminator формируют клиенты и только им известно что они обозначают. -/// Так как данные приходят редко, то их прореживания для построения графиков не предусмотрено. -/// -public interface ITimestampedSetRepository -{ - /// - /// Количество записей по указанному набору в БД. Для пагинации. - /// - /// Дискриминатор (идентификатор) набора - /// - /// - Task Count(Guid idDiscriminator, CancellationToken token); - - /// - /// Получение данных с фильтрацией. Значение фильтра null - отключен - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр позднее даты - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора - /// - /// - /// - /// - Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); - - /// - /// Диапазон дат за которые есть данные - /// - /// Дискриминатор (идентификатор) набора - /// - /// - Task GetDatesRange(Guid idDiscriminator, CancellationToken token); - - /// - /// Получить последние данные - /// - /// Дискриминатор (идентификатор) набора - /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора - /// - /// - /// - Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); - - /// - /// Добавление новых данных - /// - /// Дискриминатор (идентификатор) набора - /// - /// - /// - Task AddRange(Guid idDiscriminator, IEnumerable sets, CancellationToken token); -} \ No newline at end of file diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index 447d197..5a508c4 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -1,5 +1,6 @@ using DD.Persistence.Models; using DD.Persistence.ModelsAbstractions; +using DD.Persistence.RepositoriesAbstractions; namespace DD.Persistence.Repositories; @@ -7,14 +8,45 @@ namespace DD.Persistence.Repositories; /// Интерфейс по работе с временными данными /// /// -public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository +public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBaseRepository where TDto : class, ITimestampAbstractDto, new() { /// /// Добавление записей /// + /// /// /// /// - Task AddRange(IEnumerable dtos, CancellationToken token); + Task AddRange(Guid idDiscriminator, IEnumerable dtos, CancellationToken token); + + /// + /// Количество записей по указанному набору в БД. Для пагинации. + /// + /// Дискриминатор (идентификатор) набора + /// + /// + Task Count(Guid idDiscriminator, CancellationToken token); + + /// + /// Получить последние данные + /// + /// Дискриминатор (идентификатор) набора + /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора + /// + /// + /// + Task> GetLast(Guid idDiscriminator, IEnumerable? columnNames, int take, CancellationToken token); + + /// + /// Получение данных с фильтрацией. Значение фильтра null - отключен + /// + /// Дискриминатор (идентификатор) набора + /// Фильтр позднее даты + /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора + /// + /// + /// + /// + Task> Get(Guid idDiscriminator, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); } diff --git a/DD.Persistence/Repositories/ISyncRepository.cs b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs similarity index 60% rename from DD.Persistence/Repositories/ISyncRepository.cs rename to DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs index 9a43b01..4db2645 100644 --- a/DD.Persistence/Repositories/ISyncRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ISyncRepository.cs @@ -1,6 +1,6 @@ using DD.Persistence.Models.Common; -namespace DD.Persistence.Repositories; +namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с данными @@ -11,15 +11,18 @@ public interface ISyncRepository /// /// Получить данные, начиная с определенной даты /// + /// /// дата начала - /// /// - Task> GetGtDate(DateTimeOffset dateBegin, CancellationToken token); + /// + /// + Task> GetGtDate(Guid discriminatorId, DateTimeOffset dateBegin, CancellationToken token); /// /// Получить диапазон дат, для которых есть данные в репозитории /// + /// /// /// - Task GetDatesRange(CancellationToken token); + Task GetDatesRange(Guid discriminatorId, CancellationToken token); } diff --git a/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs similarity index 65% rename from DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs rename to DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs index 5f472e0..9fd43b4 100644 --- a/DD.Persistence/Repositories/ITimeSeriesBaseRepository.cs +++ b/DD.Persistence/RepositoriesAbstractions/ITimeSeriesBaseRepository.cs @@ -1,19 +1,23 @@ using DD.Persistence.Models; -namespace DD.Persistence.Repositories; +namespace DD.Persistence.RepositoriesAbstractions; /// /// Интерфейс по работе с прореженными данными /// -public interface ITimeSeriesBaseRepository +public interface ITimeSeriesBaseRepository { /// /// Получить список объектов с прореживанием /// + /// /// дата начала + /// /// + /// /// - Task> GetResampledData( + Task> GetResampledData( + Guid discriminatorId, DateTimeOffset dateBegin, double intervalSec = 600d, int approxPointsCount = 1024,