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

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

View File

@ -35,7 +35,8 @@ namespace DD.Persistence.Database.Postgres.Migrations
columns: table => new
{
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 =>
{

View File

@ -76,6 +76,11 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("jsonb")
.HasComment("Наименования полей в порядке индексации");
b.PrimitiveCollection<int[]>("PropTypes")
.IsRequired()
.HasColumnType("integer[]")
.HasComment("Типы полей в порядке индексации");
b.HasKey("DiscriminatorId");
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.Schema;
@ -12,4 +13,7 @@ public class DataScheme
[Comment("Наименования полей в порядке индексации"), Column(TypeName = "jsonb")]
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.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);

View File

@ -14,4 +14,9 @@ public class DataSchemeDto
/// Наименования полей
/// </summary>
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.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<IEnumerable<TimestampedValuesDto>> 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
}
/// <summary>
/// Создать спецификацию, при отсутствии таковой
/// Создать схему данных, при отсутствии таковой
/// </summary>
/// <param name="discriminatorId">Дискриминатор системы</param>
/// <param name="fieldNames">Набор наименований полей</param>
/// <param name="discriminatorId">Дискриминатор схемы</param>
/// <param name="dto">Набор данных, по образу которого будет создана соответствующая схема</param>
/// <param name="token"></param>
/// <returns></returns>
/// <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);
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}]");
}
}
/// <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>