From 1d0921c3e86b0af37907fb659568252698fd180b Mon Sep 17 00:00:00 2001 From: Roman Efremov Date: Wed, 5 Feb 2025 09:56:49 +0500 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D1=80=D0=B0=D0=B1=D0=BE=D1=82?= =?UTF-8?q?=D0=B0=D1=82=D1=8C=20=D1=81=D1=85=D0=B5=D0=BC=D1=83=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BF=D0=BE=20=D1=87=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D0=B8=20=D1=85=D1=80=D0=B0=D0=BD=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=82=D0=B8=D0=BF=D0=BE=D0=B2=20=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9=20=D0=B2=20=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D1=81=D1=82=D0=B2=D0=B8=D0=B8=20=D1=81=20=D0=B8=D0=BD?= =?UTF-8?q?=D0=B4=D0=B5=D0=BA=D1=81=D0=B0=D1=86=D0=B8=D0=B5=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ner.cs => 20250204044050_Init.Designer.cs} | 7 ++- ...3061429_Init.cs => 20250204044050_Init.cs} | 3 +- ...PersistencePostgresContextModelSnapshot.cs | 5 ++ DD.Persistence.Database/Entity/DataScheme.cs | 6 +- .../TimestampedValuesControllerTest.cs | 3 +- DD.Persistence.Models/DataSchemeDto.cs | 5 ++ .../Enumerations/PropTypeEnum.cs | 14 +++++ .../Services/TimestampedValuesService.cs | 62 ++++++++++++++----- 8 files changed, 84 insertions(+), 21 deletions(-) rename DD.Persistence.Database.Postgres/Migrations/{20250203061429_Init.Designer.cs => 20250204044050_Init.Designer.cs} (97%) rename DD.Persistence.Database.Postgres/Migrations/{20250203061429_Init.cs => 20250204044050_Init.cs} (97%) create mode 100644 DD.Persistence.Models/Enumerations/PropTypeEnum.cs diff --git a/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250204044050_Init.Designer.cs similarity index 97% rename from DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.Designer.cs rename to DD.Persistence.Database.Postgres/Migrations/20250204044050_Init.Designer.cs index bdeaf87..60e6e92 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.Designer.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250204044050_Init.Designer.cs @@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DD.Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistencePostgresContext))] - [Migration("20250203061429_Init")] + [Migration("20250204044050_Init")] partial class Init { /// @@ -79,6 +79,11 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("jsonb") .HasComment("Наименования полей в порядке индексации"); + b.PrimitiveCollection("PropTypes") + .IsRequired() + .HasColumnType("integer[]") + .HasComment("Типы полей в порядке индексации"); + b.HasKey("DiscriminatorId"); b.ToTable("data_scheme"); diff --git a/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250204044050_Init.cs similarity index 97% rename from DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.cs rename to DD.Persistence.Database.Postgres/Migrations/20250204044050_Init.cs index df996bc..24a310d 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250203061429_Init.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250204044050_Init.cs @@ -35,7 +35,8 @@ namespace DD.Persistence.Database.Postgres.Migrations columns: table => new { DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Идентификатор схемы данных"), - PropNames = table.Column(type: "jsonb", nullable: false, comment: "Наименования полей в порядке индексации") + PropNames = table.Column(type: "jsonb", nullable: false, comment: "Наименования полей в порядке индексации"), + PropTypes = table.Column(type: "integer[]", nullable: false, comment: "Типы полей в порядке индексации") }, constraints: table => { diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs index 5c5ad2e..ca319a5 100644 --- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs +++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs @@ -76,6 +76,11 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("jsonb") .HasComment("Наименования полей в порядке индексации"); + b.PrimitiveCollection("PropTypes") + .IsRequired() + .HasColumnType("integer[]") + .HasComment("Типы полей в порядке индексации"); + b.HasKey("DiscriminatorId"); b.ToTable("data_scheme"); diff --git a/DD.Persistence.Database/Entity/DataScheme.cs b/DD.Persistence.Database/Entity/DataScheme.cs index 75794c0..a231580 100644 --- a/DD.Persistence.Database/Entity/DataScheme.cs +++ b/DD.Persistence.Database/Entity/DataScheme.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using DD.Persistence.Models; +using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -12,4 +13,7 @@ public class DataScheme [Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")] public string[] PropNames { get; set; } = []; + + [Comment("Типы полей в порядке индексации")] + public PropTypeEnum[] PropTypes { get; set; } = []; } diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs index c6642a5..526b372 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs @@ -3,7 +3,6 @@ using DD.Persistence.Client.Clients; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Database.Entity; -using DD.Persistence.Extensions; using DD.Persistence.Models; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.DependencyInjection; @@ -267,7 +266,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest var count = 2048; var timestampBegin = DateTimeOffset.UtcNow; var dtos = await AddRange(discriminatorId, count); - + //act var response = await timestampedValuesClient.GetResampledData(discriminatorId, timestampBegin, count); diff --git a/DD.Persistence.Models/DataSchemeDto.cs b/DD.Persistence.Models/DataSchemeDto.cs index d1efdd5..7f238aa 100644 --- a/DD.Persistence.Models/DataSchemeDto.cs +++ b/DD.Persistence.Models/DataSchemeDto.cs @@ -14,4 +14,9 @@ public class DataSchemeDto /// Наименования полей /// public string[] PropNames { get; set; } = []; + + /// + /// Типы полей + /// + public PropTypeEnum[] PropTypes { get; set; } = []; } diff --git a/DD.Persistence.Models/Enumerations/PropTypeEnum.cs b/DD.Persistence.Models/Enumerations/PropTypeEnum.cs new file mode 100644 index 0000000..5379929 --- /dev/null +++ b/DD.Persistence.Models/Enumerations/PropTypeEnum.cs @@ -0,0 +1,14 @@ +namespace DD.Persistence.Models; + +/// +/// Типы для набора данных +/// +public enum PropTypeEnum +{ + /// + String = 0, + /// + Double = 1, + /// + DateTime = 2 +} diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs index cbcb73c..fc17777 100644 --- a/DD.Persistence/Services/TimestampedValuesService.cs +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -1,6 +1,5 @@ using DD.Persistence.Extensions; using DD.Persistence.Models; -using DD.Persistence.Models.Common; using DD.Persistence.Repositories; using DD.Persistence.Services.Interfaces; @@ -25,8 +24,7 @@ public class TimestampedValuesService : ITimestampedValuesService // ToDo: реализовать без foreach foreach (var dto in dtos) { - var keys = dto.Values.Keys.ToArray(); - await CreateSystemSpecificationIfNotExist(discriminatorId, keys, token); + await CreateDataSchemeIfNotExist(discriminatorId, dto, token); } var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token); @@ -94,7 +92,7 @@ public class TimestampedValuesService : ITimestampedValuesService public async Task> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token) { var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token); - + var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) } .ToDictionary(); var dtos = await Materialize(resultToMaterialize, token); @@ -140,37 +138,69 @@ public class TimestampedValuesService : ITimestampedValuesService } /// - /// Создать спецификацию, при отсутствии таковой + /// Создать схему данных, при отсутствии таковой /// - /// Дискриминатор системы - /// Набор наименований полей + /// Дискриминатор схемы + /// Набор данных, по образу которого будет создана соответствующая схема /// /// /// Некорректный набор наименований полей - private async Task CreateSystemSpecificationIfNotExist(Guid discriminatorId, string[] fieldNames, CancellationToken token) + private async Task CreateDataSchemeIfNotExist(Guid discriminatorId, TimestampedValuesDto dto, CancellationToken token) { - var systemSpecification = await dataSchemeRepository.Get(discriminatorId, token); - if (systemSpecification is null) + var propNames = dto.Values.Keys.ToArray(); + var propTypes = GetPropTypes(dto); + + var dataScheme = await dataSchemeRepository.Get(discriminatorId, token); + if (dataScheme is null) { - systemSpecification = new DataSchemeDto() + dataScheme = new DataSchemeDto() { DiscriminatorId = discriminatorId, - PropNames = fieldNames + PropNames = propNames, + PropTypes = propTypes }; - await dataSchemeRepository.Add(systemSpecification, token); + await dataSchemeRepository.Add(dataScheme, token); return; } - if (!systemSpecification.PropNames.SequenceEqual(fieldNames)) + if (!dataScheme.PropNames.SequenceEqual(propNames)) { - var expectedFieldNames = string.Join(", ", systemSpecification.PropNames); - var actualFieldNames = string.Join(", ", fieldNames); + var expectedFieldNames = string.Join(", ", dataScheme.PropNames); + var actualFieldNames = string.Join(", ", propNames); throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " + $"характерен набор данных: [{expectedFieldNames}], однако был передан набор: [{actualFieldNames}]"); } } + /// + /// Получить типы для набора данных в соответствии с индексацией + /// + /// + /// + /// + private PropTypeEnum[] GetPropTypes(TimestampedValuesDto dto) + { + var types = dto.Values.Select(e => + { + var valueString = e.Value.ToString(); + + if (valueString is null) + throw new ArgumentNullException("Переданный набор данных содержит null, в следствии чего не удаётся определить типы полей"); + + if (DateTimeOffset.TryParse(valueString, out _)) + return PropTypeEnum.DateTime; + + var doubleString = valueString.Replace('.', ','); + if (double.TryParse(doubleString, out _)) + return PropTypeEnum.Double; + + return PropTypeEnum.String; + }); + + return types.ToArray(); + } + /// /// Отсеить лишние поля в соответствии с заданным фильтром ///