Доработать схему данных по части хранения типов полей в соответствии с индексацией

This commit is contained in:
Roman Efremov 2025-02-05 09:56:49 +05:00
parent f955aab218
commit 1d0921c3e8
8 changed files with 84 additions and 21 deletions

View File

@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace DD.Persistence.Database.Postgres.Migrations namespace DD.Persistence.Database.Postgres.Migrations
{ {
[DbContext(typeof(PersistencePostgresContext))] [DbContext(typeof(PersistencePostgresContext))]
[Migration("20250203061429_Init")] [Migration("20250204044050_Init")]
partial class Init partial class Init
{ {
/// <inheritdoc /> /// <inheritdoc />
@ -79,6 +79,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasComment("Наименования полей в порядке индексации"); .HasComment("Наименования полей в порядке индексации");
b.PrimitiveCollection<int[]>("PropTypes")
.IsRequired()
.HasColumnType("integer[]")
.HasComment("Типы полей в порядке индексации");
b.HasKey("DiscriminatorId"); b.HasKey("DiscriminatorId");
b.ToTable("data_scheme"); b.ToTable("data_scheme");

View File

@ -35,7 +35,8 @@ namespace DD.Persistence.Database.Postgres.Migrations
columns: table => new columns: table => new
{ {
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Идентификатор схемы данных"), DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Идентификатор схемы данных"),
PropNames = table.Column<string>(type: "jsonb", nullable: false, comment: "Наименования полей в порядке индексации") PropNames = table.Column<string>(type: "jsonb", nullable: false, comment: "Наименования полей в порядке индексации"),
PropTypes = table.Column<int[]>(type: "integer[]", nullable: false, comment: "Типы полей в порядке индексации")
}, },
constraints: table => constraints: table =>
{ {

View File

@ -76,6 +76,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("jsonb") .HasColumnType("jsonb")
.HasComment("Наименования полей в порядке индексации"); .HasComment("Наименования полей в порядке индексации");
b.PrimitiveCollection<int[]>("PropTypes")
.IsRequired()
.HasColumnType("integer[]")
.HasComment("Типы полей в порядке индексации");
b.HasKey("DiscriminatorId"); b.HasKey("DiscriminatorId");
b.ToTable("data_scheme"); b.ToTable("data_scheme");

View File

@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore; using DD.Persistence.Models;
using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
@ -12,4 +13,7 @@ public class DataScheme
[Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")] [Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")]
public string[] PropNames { get; set; } = []; public string[] PropNames { get; set; } = [];
[Comment("Типы полей в порядке индексации")]
public PropTypeEnum[] PropTypes { get; set; } = [];
} }

View File

@ -3,7 +3,6 @@ using DD.Persistence.Client.Clients;
using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces;
using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Client.Clients.Interfaces.Refit;
using DD.Persistence.Database.Entity; using DD.Persistence.Database.Entity;
using DD.Persistence.Extensions;
using DD.Persistence.Models; using DD.Persistence.Models;
using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@ -267,7 +266,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest
var count = 2048; var count = 2048;
var timestampBegin = DateTimeOffset.UtcNow; var timestampBegin = DateTimeOffset.UtcNow;
var dtos = await AddRange(discriminatorId, count); var dtos = await AddRange(discriminatorId, count);
//act //act
var response = await timestampedValuesClient.GetResampledData(discriminatorId, timestampBegin, count); var response = await timestampedValuesClient.GetResampledData(discriminatorId, timestampBegin, count);

View File

@ -14,4 +14,9 @@ public class DataSchemeDto
/// Наименования полей /// Наименования полей
/// </summary> /// </summary>
public string[] PropNames { get; set; } = []; public string[] PropNames { get; set; } = [];
/// <summary>
/// Типы полей
/// </summary>
public PropTypeEnum[] PropTypes { get; set; } = [];
} }

View File

@ -0,0 +1,14 @@
namespace DD.Persistence.Models;
/// <summary>
/// Типы для набора данных
/// </summary>
public enum PropTypeEnum
{
/// <inheritdoc/>
String = 0,
/// <inheritdoc/>
Double = 1,
/// <inheritdoc/>
DateTime = 2
}

View File

@ -1,6 +1,5 @@
using DD.Persistence.Extensions; using DD.Persistence.Extensions;
using DD.Persistence.Models; using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Repositories; using DD.Persistence.Repositories;
using DD.Persistence.Services.Interfaces; using DD.Persistence.Services.Interfaces;
@ -25,8 +24,7 @@ public class TimestampedValuesService : ITimestampedValuesService
// ToDo: реализовать без foreach // ToDo: реализовать без foreach
foreach (var dto in dtos) foreach (var dto in dtos)
{ {
var keys = dto.Values.Keys.ToArray(); await CreateDataSchemeIfNotExist(discriminatorId, dto, token);
await CreateSystemSpecificationIfNotExist(discriminatorId, keys, token);
} }
var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token); var result = await timestampedValuesRepository.AddRange(discriminatorId, dtos, token);
@ -94,7 +92,7 @@ public class TimestampedValuesService : ITimestampedValuesService
public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token) public async Task<IEnumerable<TimestampedValuesDto>> GetGtDate(Guid discriminatorId, DateTimeOffset beginTimestamp, CancellationToken token)
{ {
var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token); var result = await timestampedValuesRepository.GetGtDate(discriminatorId, beginTimestamp, token);
var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) } var resultToMaterialize = new[] { KeyValuePair.Create(discriminatorId, result) }
.ToDictionary(); .ToDictionary();
var dtos = await Materialize(resultToMaterialize, token); var dtos = await Materialize(resultToMaterialize, token);
@ -140,37 +138,69 @@ public class TimestampedValuesService : ITimestampedValuesService
} }
/// <summary> /// <summary>
/// Создать спецификацию, при отсутствии таковой /// Создать схему данных, при отсутствии таковой
/// </summary> /// </summary>
/// <param name="discriminatorId">Дискриминатор системы</param> /// <param name="discriminatorId">Дискриминатор схемы</param>
/// <param name="fieldNames">Набор наименований полей</param> /// <param name="dto">Набор данных, по образу которого будет создана соответствующая схема</param>
/// <param name="token"></param> /// <param name="token"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="InvalidOperationException">Некорректный набор наименований полей</exception> /// <exception cref="InvalidOperationException">Некорректный набор наименований полей</exception>
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); var propNames = dto.Values.Keys.ToArray();
if (systemSpecification is null) var propTypes = GetPropTypes(dto);
var dataScheme = await dataSchemeRepository.Get(discriminatorId, token);
if (dataScheme is null)
{ {
systemSpecification = new DataSchemeDto() dataScheme = new DataSchemeDto()
{ {
DiscriminatorId = discriminatorId, DiscriminatorId = discriminatorId,
PropNames = fieldNames PropNames = propNames,
PropTypes = propTypes
}; };
await dataSchemeRepository.Add(systemSpecification, token); await dataSchemeRepository.Add(dataScheme, token);
return; return;
} }
if (!systemSpecification.PropNames.SequenceEqual(fieldNames)) if (!dataScheme.PropNames.SequenceEqual(propNames))
{ {
var expectedFieldNames = string.Join(", ", systemSpecification.PropNames); var expectedFieldNames = string.Join(", ", dataScheme.PropNames);
var actualFieldNames = string.Join(", ", fieldNames); var actualFieldNames = string.Join(", ", propNames);
throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " + throw new InvalidOperationException($"Для системы {discriminatorId.ToString()} " +
$"характерен набор данных: [{expectedFieldNames}], однако был передан набор: [{actualFieldNames}]"); $"характерен набор данных: [{expectedFieldNames}], однако был передан набор: [{actualFieldNames}]");
} }
} }
/// <summary>
/// Получить типы для набора данных в соответствии с индексацией
/// </summary>
/// <param name="dto"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
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();
}
/// <summary> /// <summary>
/// Отсеить лишние поля в соответствии с заданным фильтром /// Отсеить лишние поля в соответствии с заданным фильтром
/// </summary> /// </summary>