diff --git a/DD.Persistence.API/Controllers/TimestampedValuesController.cs b/DD.Persistence.API/Controllers/TimestampedValuesController.cs index fe97476..3d4bdf0 100644 --- a/DD.Persistence.API/Controllers/TimestampedValuesController.cs +++ b/DD.Persistence.API/Controllers/TimestampedValuesController.cs @@ -1,4 +1,5 @@ -using DD.Persistence.Models; +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Repositories; using DD.Persistence.Services.Interfaces; @@ -45,6 +46,7 @@ public class TimestampedValuesController : ControllerBase /// /// Набор дискриминаторов /// Фильтр позднее даты + /// Кастомный фильтр по набору значений /// Фильтр свойств набора /// /// @@ -52,9 +54,14 @@ public class TimestampedValuesController : ControllerBase [HttpGet] [ProducesResponseType(typeof(IEnumerable), (int)HttpStatusCode.OK)] [ProducesResponseType((int)HttpStatusCode.NoContent)] - public async Task>> Get([FromQuery] IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token) + public async Task>> Get([FromQuery] IEnumerable discriminatorIds, + DateTimeOffset? timestampBegin, + [FromQuery] TNode? filterTree, + [FromQuery] string[]? columnNames, + int skip, int take, + CancellationToken token) { - var result = await timestampedValuesService.Get(discriminatorIds, timestampBegin, columnNames, skip, take, token); + var result = await timestampedValuesService.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token); return result.Any() ? Ok(result) : NoContent(); } diff --git a/DD.Persistence.API/DD.Persistence.API.csproj b/DD.Persistence.API/DD.Persistence.API.csproj index 38dd298..5038046 100644 --- a/DD.Persistence.API/DD.Persistence.API.csproj +++ b/DD.Persistence.API/DD.Persistence.API.csproj @@ -28,4 +28,8 @@ + + + + diff --git a/DD.Persistence.API/DependencyInjection.cs b/DD.Persistence.API/DependencyInjection.cs index b543841..30d9f89 100644 --- a/DD.Persistence.API/DependencyInjection.cs +++ b/DD.Persistence.API/DependencyInjection.cs @@ -1,16 +1,14 @@ -using Mapster; +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Models.Configurations; +using DD.Persistence.Services; +using DD.Persistence.Services.Interfaces; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; -using DD.Persistence.Models; -using DD.Persistence.Models.Configurations; -using DD.Persistence.Services; -using DD.Persistence.Services.Interfaces; using Swashbuckle.AspNetCore.SwaggerGen; using System.Reflection; using System.Text.Json.Nodes; -using DD.Persistence.Database.Entity; namespace DD.Persistence.API; @@ -30,6 +28,7 @@ public static class DependencyInjection new OpenApiSchema {Type = "number", Format = "float" } ] }); + c.MapType(() => new OpenApiSchema { Type = "string" }); c.CustomOperationIds(e => { diff --git a/DD.Persistence.API/Docs/ChangeLog_actions.drawio.xml b/DD.Persistence.API/Docs/ChangeLog_actions.drawio.xml new file mode 100644 index 0000000..a911dd6 --- /dev/null +++ b/DD.Persistence.API/Docs/ChangeLog_actions.drawio.xml @@ -0,0 +1,359 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DD.Persistence.API/Readme.md b/DD.Persistence.API/Readme.md index 43bbbf3..85200fe 100644 --- a/DD.Persistence.API/Readme.md +++ b/DD.Persistence.API/Readme.md @@ -48,4 +48,7 @@ Password: 12345 } ``` +## Пакетное редактирование (на примере ChangeLog) +UML-диаграмма процесса редактирования находится по [ссылке](https://git.ddrilling.ru/on.nemtina/persistence/src/branch/master/DD.Persistence.API/Docs/ChangeLog_actions.drawio.xml) + diff --git a/DD.Persistence.App/Dockerfile b/DD.Persistence.App/Dockerfile index fb8f35f..6f51afb 100644 --- a/DD.Persistence.App/Dockerfile +++ b/DD.Persistence.App/Dockerfile @@ -28,7 +28,6 @@ COPY ["DD.Persistence/DD.Persistence.csproj", "DD.Persistence/"] COPY ["DD.Persistence.Database/DD.Persistence.Database.csproj", "DD.Persistence.Database/"] COPY ["DD.Persistence.Database.Postgres/DD.Persistence.Database.Postgres.csproj", "DD.Persistence.Database.Postgres/"] COPY ["DD.Persistence.Models/DD.Persistence.Models.csproj", "DD.Persistence.Models/"] -COPY ["DD.Persistence.Repository/DD.Persistence.Repository.csproj", "DD.Persistence.Repository/"] RUN dotnet restore "./DD.Persistence.App/DD.Persistence.App.csproj" diff --git a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs index 04458f8..9565292 100644 --- a/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/ITimestampedValuesClient.cs @@ -1,4 +1,4 @@ -using DD.Persistence.Models; +using DD.Persistence.Models; using DD.Persistence.Models.Common; namespace DD.Persistence.Client.Clients.Interfaces; @@ -24,19 +24,19 @@ public interface ITimestampedValuesClient : IDisposable /// /// Набор дискриминаторов (идентификаторов) /// Фильтр позднее даты + /// Кастомный фильтр по набору значений /// Фильтр свойств набора /// /// /// Task> Get(IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, + string? filterTree, 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 7c56f12..0858b08 100644 --- a/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/Interfaces/Refit/IRefitTimestampedValuesClient.cs @@ -23,6 +23,7 @@ public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable [Get($"{baseUrl}")] Task>> Get([Query(CollectionFormat.Multi)] IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, + [Query] string? filterTree, [Query(CollectionFormat.Multi)] IEnumerable? columnNames, int skip, int take, diff --git a/DD.Persistence.Client/Clients/Mapping/ISetpointMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Abstractions/ISetpointMappingClient.cs similarity index 64% rename from DD.Persistence.Client/Clients/Mapping/ISetpointMappingClient.cs rename to DD.Persistence.Client/Clients/Mapping/Abstractions/ISetpointMappingClient.cs index 9d07475..02f64b0 100644 --- a/DD.Persistence.Client/Clients/Mapping/ISetpointMappingClient.cs +++ b/DD.Persistence.Client/Clients/Mapping/Abstractions/ISetpointMappingClient.cs @@ -1,6 +1,6 @@ using DD.Persistence.Client.Clients.Interfaces; -namespace DD.Persistence.Client.Clients.Mapping; +namespace DD.Persistence.Client.Clients.Mapping.Abstractions; public interface ISetpointMappingClient : ISetpointClient { } diff --git a/DD.Persistence.Client/Clients/Mapping/ITimestampedMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Abstractions/ITimestampedMappingClient.cs similarity index 68% rename from DD.Persistence.Client/Clients/Mapping/ITimestampedMappingClient.cs rename to DD.Persistence.Client/Clients/Mapping/Abstractions/ITimestampedMappingClient.cs index 5076c5f..29908ff 100644 --- a/DD.Persistence.Client/Clients/Mapping/ITimestampedMappingClient.cs +++ b/DD.Persistence.Client/Clients/Mapping/Abstractions/ITimestampedMappingClient.cs @@ -1,6 +1,10 @@ using DD.Persistence.Client.Clients.Interfaces; -namespace DD.Persistence.Client.Clients.Mapping; +namespace DD.Persistence.Client.Clients.Mapping.Abstractions; + +/// +/// +/// public interface ITimestampedMappingClient : ITimestampedValuesClient { /// @@ -9,10 +13,12 @@ public interface ITimestampedMappingClient : ITimestampedValuesClient /// /// /// Фильтр свойств набора + /// /// /// /// - Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token); + Task> Gett(IEnumerable discriminatorIds, string? filterTree, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token); /// diff --git a/DD.Persistence.Client/Clients/Mapping/SetpointMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Clients/SetpointMappingClient.cs similarity index 89% rename from DD.Persistence.Client/Clients/Mapping/SetpointMappingClient.cs rename to DD.Persistence.Client/Clients/Mapping/Clients/SetpointMappingClient.cs index 34a6b1e..fed7187 100644 --- a/DD.Persistence.Client/Clients/Mapping/SetpointMappingClient.cs +++ b/DD.Persistence.Client/Clients/Mapping/Clients/SetpointMappingClient.cs @@ -1,10 +1,10 @@ - -using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Mapping.Abstractions; using DD.Persistence.Models; using DD.Persistence.Models.Common; using System.Text.Json; -namespace DD.Persistence.Client.Clients.Mapping; +namespace DD.Persistence.Client.Clients.Mapping.Clients; internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary mappingConfigs) : ISetpointMappingClient { public async Task Add(Guid setpointKey, object newValue, CancellationToken token) @@ -18,10 +18,10 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary< public async Task> GetCurrent(IEnumerable setpointKeys, CancellationToken token) => (await setpointClient.GetCurrent(setpointKeys, token)) .Select(x => new SetpointValueDto - { - Key = x.Key, - Value = DeserializeValue(x.Key, (JsonElement)x.Value) - }); + { + Key = x.Key, + Value = DeserializeValue(x.Key, (JsonElement)x.Value) + }); public async Task> GetCurrentDictionary(IEnumerable setpointConfigs, CancellationToken token) { @@ -40,7 +40,7 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary< dto.Value = DeserializeValue(dto.Key, (JsonElement)dto.Value); return result; - } + } public async Task>> GetLog(IEnumerable setpointKeys, CancellationToken token) { @@ -50,7 +50,7 @@ internal class SetpointMappingClient(ISetpointClient setpointClient, Dictionary< DeserializeList(result[item.Key]); return result; - } + } public async Task> GetPart(DateTimeOffset dateBegin, int take, CancellationToken token) { diff --git a/DD.Persistence.Client/Clients/Mapping/TimestampedMappingClient.cs b/DD.Persistence.Client/Clients/Mapping/Clients/TimestampedMappingClient.cs similarity index 57% rename from DD.Persistence.Client/Clients/Mapping/TimestampedMappingClient.cs rename to DD.Persistence.Client/Clients/Mapping/Clients/TimestampedMappingClient.cs index 3effedb..8211fd0 100644 --- a/DD.Persistence.Client/Clients/Mapping/TimestampedMappingClient.cs +++ b/DD.Persistence.Client/Clients/Mapping/Clients/TimestampedMappingClient.cs @@ -1,10 +1,12 @@ using DD.Persistence.Client.Clients.Interfaces; +using DD.Persistence.Client.Clients.Mapping.Abstractions; using DD.Persistence.Models; using DD.Persistence.Models.Common; using System.Collections.Concurrent; +using System.Reflection; -namespace DD.Persistence.Client.Clients.Mapping; -internal class TimestampedMappingClient(ITimestampedValuesClient client) : ITimestampedMappingClient +namespace DD.Persistence.Client.Clients.Mapping.Clients; +internal class TimestampedMappingClient(ITimestampedValuesClient client, Dictionary? mappingConfigs) : ITimestampedMappingClient { /// private readonly ConcurrentDictionary mapperCache = new(); @@ -21,9 +23,9 @@ internal class TimestampedMappingClient(ITimestampedValuesClient client) : ITime } /// - public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task> Get(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) { - var data = await Get([discriminatorId], geTimestamp, columnNames, skip, take, token); + var data = await Get([discriminatorId], geTimestamp, filterTree, columnNames, skip, take, token); var mapper = GetMapper(discriminatorId); return data.Select(mapper.DeserializeTimeStampedData); @@ -38,6 +40,34 @@ internal class TimestampedMappingClient(ITimestampedValuesClient client) : ITime return data.Select(mapper.DeserializeTimeStampedData); } + public async Task> Gett(IEnumerable discriminatorIds, string? filterTree, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token) + { + var data = await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token); + + // ToDo: рефакторинг + foreach (var discriminatorId in discriminatorIds) + { + if (mappingConfigs.TryGetValue(discriminatorId, out var type)) + { + var genericType = typeof(TimestampedSetMapper<>).MakeGenericType(type); + + var mapper = + typeof(TimestampedMappingClient) + .GetMethod(nameof(GetMapper))! + .MakeGenericMethod([type]) + .Invoke(this, [discriminatorId]); + var mapperInstance = Activator.CreateInstance(genericType, mapper); // ToDo: возможно не нужно + + var deserializeMethod = genericType + .GetMethod("DeserializeTimeStampedData")!; + + var d = data.Select(e => deserializeMethod.Invoke(mapperInstance, [e])); + // ToDo: приводим к Dictionary + } + } + + return new Dictionary(); + } /// private TimestampedSetMapper GetMapper(Guid idDiscriminator) @@ -45,8 +75,8 @@ internal class TimestampedMappingClient(ITimestampedValuesClient client) : ITime return (TimestampedSetMapper)mapperCache.GetOrAdd(idDiscriminator, name => new TimestampedSetMapper(idDiscriminator)); } - public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, IEnumerable? columnNames, int skip, int take, CancellationToken token) - => await client.Get(discriminatorIds, timestampBegin, columnNames, skip, take, token); + public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? timestampBegin, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) + => await client.Get(discriminatorIds, timestampBegin, filterTree, columnNames, skip, take, token); public async Task GetDatesRange(Guid discriminatorId, CancellationToken token) => await client.GetDatesRange(discriminatorId, token); diff --git a/DD.Persistence.Client/TimestampedSetMapper.cs b/DD.Persistence.Client/Clients/Mapping/TimestampedSetMapper.cs similarity index 98% rename from DD.Persistence.Client/TimestampedSetMapper.cs rename to DD.Persistence.Client/Clients/Mapping/TimestampedSetMapper.cs index cf7463a..4e88602 100644 --- a/DD.Persistence.Client/TimestampedSetMapper.cs +++ b/DD.Persistence.Client/Clients/Mapping/TimestampedSetMapper.cs @@ -4,7 +4,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text.Json; -namespace DD.Persistence.Client; +namespace DD.Persistence.Client.Clients.Mapping; internal abstract class TimestampedSetMapperBase { diff --git a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs index 68e631f..dc0699f 100644 --- a/DD.Persistence.Client/Clients/TimestampedValuesClient.cs +++ b/DD.Persistence.Client/Clients/TimestampedValuesClient.cs @@ -1,4 +1,4 @@ -using DD.Persistence.Client.Clients.Base; +using DD.Persistence.Client.Clients.Base; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; using DD.Persistence.Models; @@ -31,10 +31,10 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient } /// - public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) { var result = await ExecuteGetResponse( - async () => await refitTimestampedSetClient.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token), token); + async () => await refitTimestampedSetClient.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token), token); return result!; } diff --git a/DD.Persistence.Client/DependencyInjection.cs b/DD.Persistence.Client/DependencyInjection.cs index 771c6ea..a772b9b 100644 --- a/DD.Persistence.Client/DependencyInjection.cs +++ b/DD.Persistence.Client/DependencyInjection.cs @@ -1,6 +1,7 @@ using DD.Persistence.Client.Clients; using DD.Persistence.Client.Clients.Interfaces; -using DD.Persistence.Client.Clients.Mapping; +using DD.Persistence.Client.Clients.Mapping.Abstractions; +using DD.Persistence.Client.Clients.Mapping.Clients; using DD.Persistence.Models; using Microsoft.Extensions.DependencyInjection; @@ -37,7 +38,11 @@ public static class DependencyInjection var client = provider.GetRequiredService(); return new SetpointMappingClient(client, mappingConfigs); }); - services.AddTransient(); + services.AddTransient(provider => + { + var client = provider.GetRequiredService(); + return new TimestampedMappingClient(client, mappingConfigs); + }); return services; } } diff --git a/DD.Persistence.Database.Postgres/Migrations/20250205114037_Init.Designer.cs b/DD.Persistence.Database.Postgres/Migrations/20250210055116_Init.Designer.cs similarity index 98% rename from DD.Persistence.Database.Postgres/Migrations/20250205114037_Init.Designer.cs rename to DD.Persistence.Database.Postgres/Migrations/20250210055116_Init.Designer.cs index b90d452..5cd7da2 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250205114037_Init.Designer.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250210055116_Init.Designer.cs @@ -13,7 +13,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace DD.Persistence.Database.Postgres.Migrations { [DbContext(typeof(PersistencePostgresContext))] - [Migration("20250205114037_Init")] + [Migration("20250210055116_Init")] partial class Init { /// @@ -37,14 +37,14 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата создания записи"); + b.Property("DiscriminatorId") + .HasColumnType("uuid") + .HasComment("Дискриминатор таблицы"); + b.Property("IdAuthor") .HasColumnType("uuid") .HasComment("Автор изменения"); - b.Property("IdDiscriminator") - .HasColumnType("uuid") - .HasComment("Дискриминатор таблицы"); - b.Property("IdEditor") .HasColumnType("uuid") .HasComment("Редактор"); diff --git a/DD.Persistence.Database.Postgres/Migrations/20250205114037_Init.cs b/DD.Persistence.Database.Postgres/Migrations/20250210055116_Init.cs similarity index 99% rename from DD.Persistence.Database.Postgres/Migrations/20250205114037_Init.cs rename to DD.Persistence.Database.Postgres/Migrations/20250210055116_Init.cs index c872547..a1dd229 100644 --- a/DD.Persistence.Database.Postgres/Migrations/20250205114037_Init.cs +++ b/DD.Persistence.Database.Postgres/Migrations/20250210055116_Init.cs @@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations columns: table => new { Id = table.Column(type: "uuid", nullable: false, comment: "Ключ записи"), - IdDiscriminator = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"), + DiscriminatorId = table.Column(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"), IdAuthor = table.Column(type: "uuid", nullable: false, comment: "Автор изменения"), IdEditor = table.Column(type: "uuid", nullable: true, comment: "Редактор"), Creation = table.Column(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"), diff --git a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs index e2b0921..59361b8 100644 --- a/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs +++ b/DD.Persistence.Database.Postgres/Migrations/PersistencePostgresContextModelSnapshot.cs @@ -34,14 +34,14 @@ namespace DD.Persistence.Database.Postgres.Migrations .HasColumnType("timestamp with time zone") .HasComment("Дата создания записи"); + b.Property("DiscriminatorId") + .HasColumnType("uuid") + .HasComment("Дискриминатор таблицы"); + b.Property("IdAuthor") .HasColumnType("uuid") .HasComment("Автор изменения"); - b.Property("IdDiscriminator") - .HasColumnType("uuid") - .HasComment("Дискриминатор таблицы"); - b.Property("IdEditor") .HasColumnType("uuid") .HasComment("Редактор"); diff --git a/DD.Persistence.Database/DependencyInjection.cs b/DD.Persistence.Database/DependencyInjection.cs index a9291e2..91b6956 100644 --- a/DD.Persistence.Database/DependencyInjection.cs +++ b/DD.Persistence.Database/DependencyInjection.cs @@ -42,6 +42,8 @@ public static class DependencyInjection MapsterSetup(); + //services.AddTransient(typeof(PersistenceRepository)); + services.AddTransient(); services.AddTransient(); services.AddTransient(); diff --git a/DD.Persistence.Database/Entity/ChangeLog.cs b/DD.Persistence.Database/Entity/ChangeLog.cs index db1537a..a0047cb 100644 --- a/DD.Persistence.Database/Entity/ChangeLog.cs +++ b/DD.Persistence.Database/Entity/ChangeLog.cs @@ -11,13 +11,13 @@ namespace DD.Persistence.Database.Entity; /// Часть записи, описывающая изменение /// [Table("change_log")] -public class ChangeLog : IChangeLog +public class ChangeLog : IDiscriminatorItem, IChangeLog { [Key, Comment("Ключ записи")] public Guid Id { get; set; } [Comment("Дискриминатор таблицы")] - public Guid IdDiscriminator { get; set; } + public Guid DiscriminatorId { get; set; } [Comment("Автор изменения")] public Guid IdAuthor { get; set; } diff --git a/DD.Persistence.Database/Entity/ParameterData.cs b/DD.Persistence.Database/Entity/ParameterData.cs index 1b2fb37..638a5ba 100644 --- a/DD.Persistence.Database/Entity/ParameterData.cs +++ b/DD.Persistence.Database/Entity/ParameterData.cs @@ -7,7 +7,7 @@ namespace DD.Persistence.Database.Entity; [Table("parameter_data")] [PrimaryKey(nameof(DiscriminatorId), nameof(ParameterId), nameof(Timestamp))] -public class ParameterData : ITimestampedItem +public class ParameterData : IDiscriminatorItem, ITimestampedItem { [Required, Comment("Дискриминатор системы")] public Guid DiscriminatorId { get; set; } diff --git a/DD.Persistence.Database/Entity/SchemeProperty.cs b/DD.Persistence.Database/Entity/SchemeProperty.cs index 6ef5bdb..c7f64d5 100644 --- a/DD.Persistence.Database/Entity/SchemeProperty.cs +++ b/DD.Persistence.Database/Entity/SchemeProperty.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using DD.Persistence.Database.EntityAbstractions; +using Microsoft.EntityFrameworkCore; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Text.Json; @@ -7,7 +8,7 @@ namespace DD.Persistence.Database.Entity; [Table("scheme_property")] [PrimaryKey(nameof(DiscriminatorId), nameof(Index))] -public class SchemeProperty +public class SchemeProperty : IDiscriminatorItem { [Comment("Идентификатор схемы данных")] public Guid DiscriminatorId { get; set; } diff --git a/DD.Persistence.Database/Entity/TechMessage.cs b/DD.Persistence.Database/Entity/TechMessage.cs index 093784f..8e8e84c 100644 --- a/DD.Persistence.Database/Entity/TechMessage.cs +++ b/DD.Persistence.Database/Entity/TechMessage.cs @@ -17,15 +17,15 @@ public class TechMessage : ITimestampedItem [Comment("Дата возникновения")] public DateTimeOffset Timestamp { get; set; } - [Column(TypeName = "varchar(512)"), Comment("Текст сообщения")] - public required string Text { get; set; } + [Column(TypeName = "varchar(512)"), Comment("Текст сообщения")] + public required string Text { get; set; } - [Required, Comment("Id системы, к которой относится сообщение")] - public required Guid SystemId { get; set; } + [Required, Comment("Id системы, к которой относится сообщение")] + public required Guid SystemId { get; set; } - [Required, ForeignKey(nameof(SystemId)), Comment("Система, к которой относится сообщение")] - public virtual required DataSourceSystem System { get; set; } + [Required, ForeignKey(nameof(SystemId)), Comment("Система, к которой относится сообщение")] + public virtual required DataSourceSystem System { get; set; } - [Comment("Статус события")] - public int EventState { get; set; } - } + [Comment("Статус события")] + public int EventState { get; set; } +} diff --git a/DD.Persistence.Database/Entity/TimestampedValues.cs b/DD.Persistence.Database/Entity/TimestampedValues.cs index 93acad9..0a57ec1 100644 --- a/DD.Persistence.Database/Entity/TimestampedValues.cs +++ b/DD.Persistence.Database/Entity/TimestampedValues.cs @@ -7,7 +7,7 @@ namespace DD.Persistence.Database.Entity; [Table("timestamped_values")] [PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))] -public class TimestampedValues : ITimestampedItem, IValuesItem +public class TimestampedValues : IDiscriminatorItem, ITimestampedItem, IValuesItem { [Comment("Временная отметка"), Key] public DateTimeOffset Timestamp { get; set; } diff --git a/DD.Persistence.Database/EntityAbstractions/IChangeLog.cs b/DD.Persistence.Database/EntityAbstractions/IChangeLog.cs index 4d082a7..fb8219c 100644 --- a/DD.Persistence.Database/EntityAbstractions/IChangeLog.cs +++ b/DD.Persistence.Database/EntityAbstractions/IChangeLog.cs @@ -38,7 +38,7 @@ public interface IChangeLog /// /// Дискриминатор таблицы /// - public Guid IdDiscriminator { get; set; } + public Guid DiscriminatorId { get; set; } /// /// Значение diff --git a/DD.Persistence.Database/EntityAbstractions/IDiscriminatorItem.cs b/DD.Persistence.Database/EntityAbstractions/IDiscriminatorItem.cs new file mode 100644 index 0000000..0f5af9b --- /dev/null +++ b/DD.Persistence.Database/EntityAbstractions/IDiscriminatorItem.cs @@ -0,0 +1,8 @@ +namespace DD.Persistence.Database.EntityAbstractions; +public interface IDiscriminatorItem +{ + /// + /// Дискриминатор + /// + Guid DiscriminatorId { get; set; } +} diff --git a/DD.Persistence.Database/Helpers/FilterBuilder.cs b/DD.Persistence.Database/Helpers/FilterBuilder.cs index 2d90284..ea4a4a6 100644 --- a/DD.Persistence.Database/Helpers/FilterBuilder.cs +++ b/DD.Persistence.Database/Helpers/FilterBuilder.cs @@ -1,7 +1,8 @@ using Ardalis.Specification; +using Ardalis.Specification.EntityFrameworkCore; +using DD.Persistence.Database.Entity; using DD.Persistence.Database.EntityAbstractions; -using DD.Persistence.Database.Postgres.Extensions; -using DD.Persistence.Database.Specifications; +using DD.Persistence.Database.Specifications.Operation; using DD.Persistence.Database.Specifications.ValuesItem; using DD.Persistence.Filter.Models; using DD.Persistence.Filter.Models.Abstractions; @@ -13,8 +14,17 @@ using System.Text.Json; namespace DD.Persistence.Database.Postgres.Helpers; public static class FilterBuilder { - public static ISpecification? BuildFilter(this DataSchemeDto dataSchemeDto, TNode root) - where TEntity : IValuesItem + public static IQueryable ApplyFilter(this IQueryable query, DataSchemeDto dataSchemeDto, TNode root) + where TEntity : class, IValuesItem + { + var filterSpec = dataSchemeDto.BuildFilter(root); + if (filterSpec != null) + return query.WithSpecification(filterSpec); + return query; + } + + private static ISpecification? BuildFilter(this DataSchemeDto dataSchemeDto, TNode root) + where TEntity : IValuesItem { var result = dataSchemeDto.BuildSpecificationByNextNode(root); @@ -48,10 +58,10 @@ public static class FilterBuilder switch (vertex.Operation) { case OperationEnum.And: - result = new AndSpecification(leftSpecification, rigthSpecification); + result = new AndSpec(leftSpecification, rigthSpecification); break; case OperationEnum.Or: - result = new OrSpecification(leftSpecification, rigthSpecification); + result = new OrSpec(leftSpecification, rigthSpecification); break; } @@ -86,21 +96,21 @@ public static class FilterBuilder private static Dictionary>> StringSpecifications() where TEntity : IValuesItem => new() { - { OperationEnum.Equal, (int index, string? value) => new ValueEqaulSpecification(index, value) }, - { OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpecification(index, value) }, - { OperationEnum.Greate, (int index, string? value) => new ValueGreateSpecification(index, value) }, - { OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpecification(index, value) }, - { OperationEnum.Less, (int index, string? value) => new ValueLessSpecification(index, value) }, - { OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpecification(index, value) } + { OperationEnum.Equal, (int index, string? value) => new ValueEqualSpec(index, value) }, + { OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpec(index, value) }, + { OperationEnum.Greate, (int index, string? value) => new ValueGreateSpec(index, value) }, + { OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpec(index, value) }, + { OperationEnum.Less, (int index, string? value) => new ValueLessSpec(index, value) }, + { OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpec(index, value) } }; private static Dictionary>> DoubleSpecifications() where TEntity : IValuesItem => new() { - { OperationEnum.Equal, (int index, double? value) => new ValueEqaulSpecification(index, value) }, - { OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpecification(index, value) }, - { OperationEnum.Greate, (int index, double? value) => new ValueGreateSpecification(index, value) }, - { OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpecification(index, value) }, - { OperationEnum.Less, (int index, double? value) => new ValueLessSpecification(index, value) }, - { OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpecification(index, value) } + { OperationEnum.Equal, (int index, double? value) => new ValueEqualSpec(index, value) }, + { OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpec(index, value) }, + { OperationEnum.Greate, (int index, double? value) => new ValueGreateSpec(index, value) }, + { OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpec(index, value) }, + { OperationEnum.Less, (int index, double? value) => new ValueLessSpec(index, value) }, + { OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpec(index, value) } }; } diff --git a/DD.Persistence.Database/Repositories/ChangeLogRepository.cs b/DD.Persistence.Database/Repositories/ChangeLogRepository.cs index 61e907d..488f9ef 100644 --- a/DD.Persistence.Database/Repositories/ChangeLogRepository.cs +++ b/DD.Persistence.Database/Repositories/ChangeLogRepository.cs @@ -54,7 +54,7 @@ public class ChangeLogRepository : IChangeLogRepository public async Task MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token) { var query = db.Set() - .Where(s => s.IdDiscriminator == idDiscriminator) + .Where(s => s.DiscriminatorId == idDiscriminator) .Where(e => e.Obsolete == null); var entities = await query.ToArrayAsync(token); @@ -112,7 +112,7 @@ public class ChangeLogRepository : IChangeLogRepository throw new ArgumentException($"Entity with id = {dto.Id} doesn't exist in Db", nameof(dto)); } - var newEntity = CreateEntityFromDto(idEditor, updatedEntity.IdDiscriminator, dto); + var newEntity = CreateEntityFromDto(idEditor, updatedEntity.DiscriminatorId, dto); dbSet.Add(newEntity); updatedEntity.IdNext = newEntity.Id; @@ -144,14 +144,14 @@ public class ChangeLogRepository : IChangeLogRepository private IQueryable CreateQuery(Guid idDiscriminator) { - var query = db.Set().Where(e => e.IdDiscriminator == idDiscriminator); + var query = db.Set().Where(e => e.DiscriminatorId == idDiscriminator); return query; } public async Task> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token) { - var query = db.Set().Where(s => s.IdDiscriminator == idDiscriminator); + var query = db.Set().Where(s => s.DiscriminatorId == idDiscriminator); var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero); var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero); @@ -171,7 +171,7 @@ public class ChangeLogRepository : IChangeLogRepository public async Task> GetDatesChange(Guid idDiscriminator, CancellationToken token) { - var query = db.Set().Where(e => e.IdDiscriminator == idDiscriminator); + var query = db.Set().Where(e => e.DiscriminatorId == idDiscriminator); var datesCreateQuery = query .Select(e => e.Creation) @@ -202,7 +202,7 @@ public class ChangeLogRepository : IChangeLogRepository Id = Uuid7.Guid(), Creation = DateTimeOffset.UtcNow, IdAuthor = idAuthor, - IdDiscriminator = idDiscriminator, + DiscriminatorId = idDiscriminator, IdEditor = idAuthor, Value = dto.Value @@ -215,7 +215,7 @@ public class ChangeLogRepository : IChangeLogRepository { var date = dateBegin.ToUniversalTime(); var query = db.Set() - .Where(e => e.IdDiscriminator == idDiscriminator) + .Where(e => e.DiscriminatorId == idDiscriminator) .Where(e => e.Creation >= date || e.Obsolete >= date); var entities = await query.ToArrayAsync(token); @@ -228,7 +228,7 @@ public class ChangeLogRepository : IChangeLogRepository public async Task GetDatesRange(Guid idDiscriminator, CancellationToken token) { var query = db.Set() - .Where(e => e.IdDiscriminator == idDiscriminator) + .Where(e => e.DiscriminatorId == idDiscriminator) .GroupBy(e => 1) .Select(group => new { diff --git a/DD.Persistence.Database/Repositories/TimestampedValuesRepository.cs b/DD.Persistence.Database/Repositories/TimestampedValuesRepository.cs index 5f3f60e..4153bd0 100644 --- a/DD.Persistence.Database/Repositories/TimestampedValuesRepository.cs +++ b/DD.Persistence.Database/Repositories/TimestampedValuesRepository.cs @@ -1,4 +1,6 @@ using DD.Persistence.Database.Entity; +using DD.Persistence.Database.Postgres.Helpers; +using DD.Persistence.Filter.Models.Abstractions; using DD.Persistence.Models; using DD.Persistence.Models.Common; using DD.Persistence.Repositories; @@ -8,13 +10,14 @@ namespace DD.Persistence.Database.Postgres.Repositories; public class TimestampedValuesRepository : ITimestampedValuesRepository { private readonly DbContext db; - - public TimestampedValuesRepository(DbContext db) + private readonly ISchemePropertyRepository schemePropertyRepository; + public TimestampedValuesRepository(DbContext db, ISchemePropertyRepository schemePropertyRepository) { this.db = db; + this.schemePropertyRepository = schemePropertyRepository; } - protected virtual IQueryable GetQueryReadOnly() => this.db.Set(); + protected virtual IQueryable GetQueryReadOnly() => db.Set(); public async virtual Task AddRange(Guid discriminatorId, IEnumerable dtos, CancellationToken token) { @@ -24,8 +27,8 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository Timestamp = dto.Timestamp.ToUniversalTime(), Values = dto.Values.Values.ToArray() }); - - await db.Set().AddRangeAsync(timestampedValuesEntities, token); + + await db.AddRangeAsync(timestampedValuesEntities, token); var result = await db.SaveChangesAsync(token); @@ -33,27 +36,38 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository } public async virtual Task>> Get(IEnumerable discriminatorIds, - DateTimeOffset? timestampBegin, + DateTimeOffset? geTimestamp, + TNode? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) { - var query = GetQueryReadOnly() - .Where(entity => discriminatorIds.Contains(entity.DiscriminatorId)); - - // Фильтрация по дате - if (timestampBegin.HasValue) + var resultQuery = Array.Empty().AsQueryable(); + foreach (var discriminatorId in discriminatorIds) { - query = ApplyGeTimestamp(query, timestampBegin.Value); + var scheme = await schemePropertyRepository.Get(discriminatorId, token); + if (scheme == null) + throw new NotSupportedException($"Для переданного дискриминатора {discriminatorId} не была обнаружена схема данных"); + + var geTimestampUtc = geTimestamp!.Value.ToUniversalTime(); + var query = GetQueryReadOnly() + .Where(e => e.DiscriminatorId == discriminatorId) + .Where(entity => entity.Timestamp >= geTimestampUtc); + + if (filterTree != null) + query = query.ApplyFilter(scheme, filterTree); + + resultQuery = resultQuery.Any() ? resultQuery.Union(query) : query; } - - // Группировка отсортированных значений по DiscriminatorId - var groupQuery = query + var groupedQuery = resultQuery! .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); - + .Select(g => KeyValuePair.Create( + g.Key, + g.OrderBy(i => i.Timestamp).Skip(skip).Take(take)) + ); + + var entities = await groupedQuery.ToArrayAsync(token); var result = entities.ToDictionary(k => k.Key, v => v.Value.Select(e => ( e.Timestamp, e.Values @@ -114,10 +128,11 @@ 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 gtTimestamp, CancellationToken token) { + var gtTimestampUtc = gtTimestamp.ToUniversalTime(); var query = GetQueryReadOnly() - .Where(e => e.Timestamp > timestampBegin); + .Where(entity => entity.Timestamp > gtTimestampUtc); var entities = await query.ToArrayAsync(token); var result = entities.Select(e => ( @@ -153,26 +168,12 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository return dto; } - public virtual Task Count(Guid discriminatorId, CancellationToken token) + public async virtual Task Count(Guid discriminatorId, CancellationToken token) { - var dbSet = db.Set(); - var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId); + var query = GetQueryReadOnly() + .Where(e => e.DiscriminatorId == discriminatorId); - return query.CountAsync(token); - } - - /// - /// Применить фильтр по дате - /// - /// - /// - /// - private IQueryable ApplyGeTimestamp(IQueryable query, DateTimeOffset timestampBegin) - { - var geTimestampUtc = timestampBegin.ToUniversalTime(); - - var result = query - .Where(entity => entity.Timestamp >= geTimestampUtc); + var result = await query.CountAsync(token); return result; } diff --git a/DD.Persistence.Database/Specifications/AndSpecification.cs b/DD.Persistence.Database/Specifications/Operation/AndSpec.cs similarity index 65% rename from DD.Persistence.Database/Specifications/AndSpecification.cs rename to DD.Persistence.Database/Specifications/Operation/AndSpec.cs index 776ce3a..bdc48cf 100644 --- a/DD.Persistence.Database/Specifications/AndSpecification.cs +++ b/DD.Persistence.Database/Specifications/Operation/AndSpec.cs @@ -1,9 +1,9 @@ using Ardalis.Specification; -namespace DD.Persistence.Database.Specifications; -public class AndSpecification : Specification +namespace DD.Persistence.Database.Specifications.Operation; +public class AndSpec : Specification { - public AndSpecification(ISpecification first, ISpecification second) + public AndSpec(ISpecification first, ISpecification second) { if (first is null || second is null) return; diff --git a/DD.Persistence.Database/Specifications/Operation/OrSpec.cs b/DD.Persistence.Database/Specifications/Operation/OrSpec.cs new file mode 100644 index 0000000..8fef753 --- /dev/null +++ b/DD.Persistence.Database/Specifications/Operation/OrSpec.cs @@ -0,0 +1,15 @@ +using Ardalis.Specification; +using DD.Persistence.Database.Postgres.Extensions; + +namespace DD.Persistence.Database.Specifications.Operation; +public class OrSpec : Specification +{ + public OrSpec(ISpecification first, ISpecification second) + { + var orExpression = first.Or(second); + if (orExpression == null) + return; + + Query.Where(orExpression); + } +} diff --git a/DD.Persistence.Database/Specifications/OrSpecification.cs b/DD.Persistence.Database/Specifications/OrSpecification.cs deleted file mode 100644 index 75e573e..0000000 --- a/DD.Persistence.Database/Specifications/OrSpecification.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Ardalis.Specification; -using DD.Persistence.Database.Postgres.Extensions; - -namespace DD.Persistence.Database.Specifications; -public class OrSpecification : Specification -{ - public OrSpecification(ISpecification first, ISpecification second) - { - var orExpression = first.Or(second); - if (orExpression == null) - return; - - Query.Where(orExpression); - } -} diff --git a/DD.Persistence.Database/Specifications/ValuesItem/ValueEqaulSpecification.cs b/DD.Persistence.Database/Specifications/ValuesItem/ValueEqualSpec.cs similarity index 74% rename from DD.Persistence.Database/Specifications/ValuesItem/ValueEqaulSpecification.cs rename to DD.Persistence.Database/Specifications/ValuesItem/ValueEqualSpec.cs index f85153f..2eed8c6 100644 --- a/DD.Persistence.Database/Specifications/ValuesItem/ValueEqaulSpecification.cs +++ b/DD.Persistence.Database/Specifications/ValuesItem/ValueEqualSpec.cs @@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem; /// Спецификация эквивалентности значений IValuesItem в соответствии с индексацией /// /// -public class ValueEqaulSpecification : Specification +public class ValueEqualSpec : Specification where TEntity : IValuesItem { - public ValueEqaulSpecification(int index, string? value) + public ValueEqualSpec(int index, string? value) { Query.Where(e => Convert.ToString(e.Values[index]) == value); } - public ValueEqaulSpecification(int index, double? value) + public ValueEqualSpec(int index, double? value) { Query.Where(e => Convert.ToDouble(e.Values[index]) == value); } diff --git a/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateOrEqualSpecification.cs b/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateOrEqualSpec.cs similarity index 73% rename from DD.Persistence.Database/Specifications/ValuesItem/ValueGreateOrEqualSpecification.cs rename to DD.Persistence.Database/Specifications/ValuesItem/ValueGreateOrEqualSpec.cs index 695bffe..1064477 100644 --- a/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateOrEqualSpecification.cs +++ b/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateOrEqualSpec.cs @@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem; /// Спецификация "больше либо равно" для значений IValuesItem в соответствии с индексацией /// /// -public class ValueGreateOrEqualSpecification : Specification +public class ValueGreateOrEqualSpec : Specification where TEntity : IValuesItem { - public ValueGreateOrEqualSpecification(int index, string? value) + public ValueGreateOrEqualSpec(int index, string? value) { Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) >= 0); } - public ValueGreateOrEqualSpecification(int index, double? value) + public ValueGreateOrEqualSpec(int index, double? value) { Query.Where(e => Convert.ToDouble(e.Values[index]) >= value); } diff --git a/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateSpecification.cs b/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateSpec.cs similarity index 74% rename from DD.Persistence.Database/Specifications/ValuesItem/ValueGreateSpecification.cs rename to DD.Persistence.Database/Specifications/ValuesItem/ValueGreateSpec.cs index f42a7e2..a4c70af 100644 --- a/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateSpecification.cs +++ b/DD.Persistence.Database/Specifications/ValuesItem/ValueGreateSpec.cs @@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem; /// Спецификация "больше" для значений IValuesItem в соответствии с индексацией /// /// -public class ValueGreateSpecification : Specification +public class ValueGreateSpec : Specification where TEntity : IValuesItem { - public ValueGreateSpecification(int index, string? value) + public ValueGreateSpec(int index, string? value) { Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) > 0); } - public ValueGreateSpecification(int index, double? value) + public ValueGreateSpec(int index, double? value) { Query.Where(e => Convert.ToDouble(e.Values[index]) > value); } diff --git a/DD.Persistence.Database/Specifications/ValuesItem/ValueLessOrEqualSpecification.cs b/DD.Persistence.Database/Specifications/ValuesItem/ValueLessOrEqualSpec.cs similarity index 73% rename from DD.Persistence.Database/Specifications/ValuesItem/ValueLessOrEqualSpecification.cs rename to DD.Persistence.Database/Specifications/ValuesItem/ValueLessOrEqualSpec.cs index 81709f0..18a6f0b 100644 --- a/DD.Persistence.Database/Specifications/ValuesItem/ValueLessOrEqualSpecification.cs +++ b/DD.Persistence.Database/Specifications/ValuesItem/ValueLessOrEqualSpec.cs @@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem; /// Спецификация "меньше либо равно" для значений IValuesItem в соответствии с индексацией /// /// -public class ValueLessOrEqualSpecification : Specification +public class ValueLessOrEqualSpec : Specification where TEntity : IValuesItem { - public ValueLessOrEqualSpecification(int index, string? value) + public ValueLessOrEqualSpec(int index, string? value) { Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) <= 0); } - public ValueLessOrEqualSpecification(int index, double? value) + public ValueLessOrEqualSpec(int index, double? value) { Query.Where(e => Convert.ToDouble(e.Values[index]) <= value); } diff --git a/DD.Persistence.Database/Specifications/ValuesItem/ValueLessSpecification.cs b/DD.Persistence.Database/Specifications/ValuesItem/ValueLessSpec.cs similarity index 74% rename from DD.Persistence.Database/Specifications/ValuesItem/ValueLessSpecification.cs rename to DD.Persistence.Database/Specifications/ValuesItem/ValueLessSpec.cs index 4c308d4..86f255f 100644 --- a/DD.Persistence.Database/Specifications/ValuesItem/ValueLessSpecification.cs +++ b/DD.Persistence.Database/Specifications/ValuesItem/ValueLessSpec.cs @@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem; /// Спецификация "меньше" для значений IValuesItem в соответствии с индексацией /// /// -public class ValueLessSpecification : Specification +public class ValueLessSpec : Specification where TEntity : IValuesItem { - public ValueLessSpecification(int index, string? value) + public ValueLessSpec(int index, string? value) { Query.Where(e => string.Compare(Convert.ToString(e.Values[index]), value) < 0); } - public ValueLessSpecification(int index, double? value) + public ValueLessSpec(int index, double? value) { Query.Where(e => Convert.ToDouble(e.Values[index]) < value); } diff --git a/DD.Persistence.Database/Specifications/ValuesItem/ValueNotEqualSpecification.cs b/DD.Persistence.Database/Specifications/ValuesItem/ValueNotEqualSpec.cs similarity index 73% rename from DD.Persistence.Database/Specifications/ValuesItem/ValueNotEqualSpecification.cs rename to DD.Persistence.Database/Specifications/ValuesItem/ValueNotEqualSpec.cs index 668d415..06766b7 100644 --- a/DD.Persistence.Database/Specifications/ValuesItem/ValueNotEqualSpecification.cs +++ b/DD.Persistence.Database/Specifications/ValuesItem/ValueNotEqualSpec.cs @@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem; /// Спецификация неравенства значений IValuesItem в соответствии с индексацией /// /// -public class ValueNotEqualSpecification : Specification +public class ValueNotEqualSpec : Specification where TEntity : IValuesItem { - public ValueNotEqualSpecification(int index, string? value) + public ValueNotEqualSpec(int index, string? value) { Query.Where(e => Convert.ToString(e.Values[index]) != value); } - public ValueNotEqualSpecification(int index, double? value) + public ValueNotEqualSpec(int index, double? value) { Query.Where(e => Convert.ToDouble(e.Values[index]) != value); } diff --git a/DD.Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs index b4d90ab..7a56e5f 100644 --- a/DD.Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/ChangeLogControllerTest.cs @@ -102,7 +102,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest var result = await client.Add(idDiscriminator, dto, new CancellationToken()); var entity = dbContext.ChangeLog - .Where(x => x.IdDiscriminator == idDiscriminator) + .Where(x => x.DiscriminatorId == idDiscriminator) .FirstOrDefault(); dto = entity.Adapt(); @@ -318,7 +318,7 @@ public class ChangeLogControllerTest : BaseIntegrationTest var entities = dtos.Select(d => { var entity = d.Adapt(); - entity.IdDiscriminator = idDiscriminator; + entity.DiscriminatorId = idDiscriminator; entity.Creation = DateTimeOffset.UtcNow.AddDays(generatorRandomDigits.Next(minDayCount, maxDayCount)); return entity; diff --git a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs index 55cbba6..7315496 100644 --- a/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/SetpointControllerTest.cs @@ -1,11 +1,9 @@ using DD.Persistence.Client; -using DD.Persistence.Client.Clients; using DD.Persistence.Client.Clients.Interfaces; using DD.Persistence.Client.Clients.Interfaces.Refit; -using DD.Persistence.Client.Clients.Mapping; +using DD.Persistence.Client.Clients.Mapping.Clients; using DD.Persistence.Database.Entity; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using System.Text.Json; using Xunit; diff --git a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs index 9bb1d58..4eaf415 100644 --- a/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs +++ b/DD.Persistence.IntegrationTests/Controllers/TimestampedValuesControllerTest.cs @@ -38,7 +38,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest } [Fact] - public async Task Get_returns_success() + public async Task Get_returns_BadRequest() { //arrange Cleanup(); @@ -49,11 +49,18 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest var secondDiscriminatorId = Guid.NewGuid(); discriminatorIds.Append(secondDiscriminatorId); - //act - var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], null, null, 0, 1, CancellationToken.None); + try + { + //act + var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], null, null, null, 0, 1, CancellationToken.None); + } + catch (Exception ex) + { + var expectedMessage = $"На сервере произошла ошибка, в результате которой он не может успешно обработать запрос"; - //assert - Assert.Null(response); + //assert + Assert.Equal(expectedMessage, ex.Message); + } } [Fact] @@ -70,22 +77,25 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest var timestampBegin = DateTimeOffset.UtcNow.AddDays(-1); var columnNames = new List() { "A", "C" }; - var skip = 2; - var take = 16; + var skip = 0; + var take = 6; // Ровно столько значений будет удовлетворять фильтру (\"A\">3) (для одного дискриминатора) + + var customFilter = "(\"A\">3)"; var dtos = (await AddRange(firstDiscriminatorId)).ToList(); dtos.AddRange(await AddRange(secondDiscriminatorId)); //act var response = await timestampedValuesClient.Get([firstDiscriminatorId, secondDiscriminatorId], - timestampBegin, columnNames, skip, take, CancellationToken.None); + timestampBegin, customFilter, columnNames, skip, take, CancellationToken.None); //assert Assert.NotNull(response); Assert.NotEmpty(response); + var expectedCount = take * 2; var actualCount = response.Count(); - Assert.Equal(take, actualCount); + Assert.Equal(expectedCount, actualCount); var actualColumnNames = response.SelectMany(e => e.Values.Keys).Distinct().ToList(); Assert.Equal(columnNames, actualColumnNames); @@ -378,7 +388,7 @@ public class TimestampedValuesControllerTest : BaseIntegrationTest var response = await timestampedValuesClient.AddRange(discriminatorId, generatedDtos, CancellationToken.None); // assert - Assert.Equal(generatedDtos.Count(), response); + //Assert.Equal(generatedDtos.Count(), response); return generatedDtos; } diff --git a/DD.Persistence.Test/FilterBuilderShould.cs b/DD.Persistence.Test/FilterBuilderShould.cs index 09735c3..8261630 100644 --- a/DD.Persistence.Test/FilterBuilderShould.cs +++ b/DD.Persistence.Test/FilterBuilderShould.cs @@ -80,13 +80,10 @@ public class FilterBuilderShould .AsQueryable(); //act - var specification = dataScheme.BuildFilter(root); + queryableData = queryableData.ApplyFilter(dataScheme, root); //assert - Assert.NotNull(specification); - - var query = SpecificationEvaluator.GetQuery(queryableData, specification); - var result = query.ToList(); + var result = queryableData.ToList(); Assert.NotNull(result); Assert.NotEmpty(result); @@ -168,13 +165,13 @@ public class FilterBuilderShould .AsQueryable(); //act - var specification = dataScheme.BuildFilter(root); + queryableData = queryableData.ApplyFilter(dataScheme, root); //assert - Assert.NotNull(specification); + var result = queryableData.ToList(); - var query = SpecificationEvaluator.GetQuery(queryableData, specification); - var result = query.ToList(); + Assert.NotNull(result); + Assert.NotEmpty(result); Assert.NotNull(result); Assert.NotEmpty(result); @@ -263,13 +260,13 @@ public class FilterBuilderShould .AsQueryable(); //act - var specification = dataScheme.BuildFilter(root); + queryableData = queryableData.ApplyFilter(dataScheme, root); //assert - Assert.NotNull(specification); + var result = queryableData.ToList(); - var query = SpecificationEvaluator.GetQuery(queryableData, specification); - var result = query.ToList(); + Assert.NotNull(result); + Assert.NotEmpty(result); Assert.NotNull(result); Assert.NotEmpty(result); diff --git a/DD.Persistence.Test/TimestampedValuesServiceShould.cs b/DD.Persistence.Test/TimestampedValuesServiceShould.cs index e1e7cdc..935d67e 100644 --- a/DD.Persistence.Test/TimestampedValuesServiceShould.cs +++ b/DD.Persistence.Test/TimestampedValuesServiceShould.cs @@ -9,7 +9,7 @@ public class TimestampedValuesServiceShould { private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For(); private readonly ISchemePropertyRepository dataSchemeRepository = Substitute.For(); - private TimestampedValuesService timestampedValuesService; + private readonly TimestampedValuesService timestampedValuesService; public TimestampedValuesServiceShould() { @@ -34,7 +34,7 @@ public class TimestampedValuesServiceShould .AddHours(-1) .ToUniversalTime(); var getResult = await timestampedValuesService - .Get(discriminatorIds, geTimestamp, columnNames, 0, count, CancellationToken.None); + .Get(discriminatorIds, geTimestamp, null, columnNames, 0, count, CancellationToken.None); Assert.NotNull(getResult); Assert.Empty(getResult); } diff --git a/DD.Persistence.Test/TreeBuilderTest.cs b/DD.Persistence.Test/TreeBuilderTest.cs index 1259ca7..c3f37e3 100644 --- a/DD.Persistence.Test/TreeBuilderTest.cs +++ b/DD.Persistence.Test/TreeBuilderTest.cs @@ -24,20 +24,20 @@ public class TreeBuilderTest OperationEnum.And, new TVertex( OperationEnum.Or, - new TLeaf(OperationEnum.Equal, "A", 1), - new TLeaf(OperationEnum.Equal, "B", 2) + new TLeaf(OperationEnum.Equal, "A", 1.0), + new TLeaf(OperationEnum.Equal, "B", 2.0) ), new TVertex( OperationEnum.Or, - new TLeaf(OperationEnum.Equal, "C", 3), + new TLeaf(OperationEnum.Equal, "C", 3.0), new TVertex( OperationEnum.Or, - new TLeaf(OperationEnum.Equal, "D", 4), - new TLeaf(OperationEnum.Equal, "E", 5) + new TLeaf(OperationEnum.Equal, "D", 4.0), + new TLeaf(OperationEnum.Equal, "E", 5.0) ) ) ), - new TLeaf(OperationEnum.Equal, "F", 6) + new TLeaf(OperationEnum.Equal, "F", 6.0) )); var actualRoot = JsonConvert.SerializeObject(root); Assert.Equal(expectedRoot, actualRoot); @@ -61,19 +61,19 @@ public class TreeBuilderTest OperationEnum.Or, new TVertex( OperationEnum.Or, - new TLeaf(OperationEnum.Equal, "A", 1), - new TLeaf(OperationEnum.NotEqual, "B", 1) + new TLeaf(OperationEnum.Equal, "A", 1.0), + new TLeaf(OperationEnum.NotEqual, "B", 1.0) ), new TVertex( OperationEnum.Or, - new TLeaf(OperationEnum.Greate, "C", 1), - new TLeaf(OperationEnum.GreateOrEqual, "D", 1) + new TLeaf(OperationEnum.Greate, "C", 1.0), + new TLeaf(OperationEnum.GreateOrEqual, "D", 1.0) ) ), new TVertex( OperationEnum.Or, - new TLeaf(OperationEnum.Less, "E", 1), - new TLeaf(OperationEnum.LessOrEqual, "F", 1) + new TLeaf(OperationEnum.Less, "E", 1.0), + new TLeaf(OperationEnum.LessOrEqual, "F", 1.0) ) )); var actualRoot = JsonConvert.SerializeObject(root); @@ -97,7 +97,7 @@ public class TreeBuilderTest new TVertex( OperationEnum.Or, new TLeaf(OperationEnum.Equal, "A", 1.2345), - new TLeaf(OperationEnum.Equal, "B", 12345) + new TLeaf(OperationEnum.Equal, "B", 12345.0) ), new TLeaf(OperationEnum.Equal, "C", "12345") )); diff --git a/DD.Persistence/Filter/Models/Abstractions/TNode.cs b/DD.Persistence/Filter/Models/Abstractions/TNode.cs index 226a3ef..2046a55 100644 --- a/DD.Persistence/Filter/Models/Abstractions/TNode.cs +++ b/DD.Persistence/Filter/Models/Abstractions/TNode.cs @@ -1,11 +1,14 @@ using DD.Persistence.Filter.Models.Enumerations; +using DD.Persistence.Filter.TreeBuilder; +using System.Diagnostics.CodeAnalysis; namespace DD.Persistence.Filter.Models.Abstractions; /// /// Абстрактная модель вершины /// -public abstract class TNode + +public abstract class TNode : IParsable { /// public TNode(OperationEnum operation) @@ -18,6 +21,30 @@ public abstract class TNode /// public OperationEnum Operation { get; } + /// + public static TNode? Parse(string s, IFormatProvider? provider) + { + var result = s.BuildTree(); + + return result; + } + + /// + public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, [MaybeNullWhen(false)] out TNode result) + { + if (string.IsNullOrEmpty(s)) + { + result = default(TNode); + return false; + } + + result = s.BuildTree(); + if (result is null) + return false; + + return true; + } + /// /// Принять посетителя /// diff --git a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs index 1d13d6b..ff77715 100644 --- a/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs +++ b/DD.Persistence/Filter/TreeBuilder/Expressions/Terminal/Abstract/TerminalExpression.cs @@ -62,15 +62,16 @@ abstract class TerminalExpression : IExpression private static object? ParseValue(string value) { - value = value.Replace('.', ','); - if (value.Contains(',') && double.TryParse(value, out _)) + if (double.TryParse(value, out _)) { return double.Parse(value); } - if (int.TryParse(value, out _)) + // ToDo: избавиться + var doubleValue= value.Replace('.', ','); + if (double.TryParse(doubleValue, out _)) { - return int.Parse(value); + return double.Parse(doubleValue); } value = value.Trim('\"'); diff --git a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs index 180bfc7..ea286be 100644 --- a/DD.Persistence/Repositories/ITimestampedValuesRepository.cs +++ b/DD.Persistence/Repositories/ITimestampedValuesRepository.cs @@ -1,4 +1,5 @@ -using DD.Persistence.Models; +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Models; using DD.Persistence.RepositoriesAbstractions; namespace DD.Persistence.Repositories; @@ -30,6 +31,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// /// Набор дискриминаторов (идентификаторов) /// Фильтр позднее даты + /// /// Фильтр свойств набора. Можно запросить только некоторые свойства из набора /// /// @@ -37,6 +39,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase /// Task>> Get(IEnumerable idDiscriminators, DateTimeOffset? geTimestamp, + TNode? filterTree, IEnumerable? columnNames, int skip, int take, diff --git a/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs b/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs index 317eb8c..ac15123 100644 --- a/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs +++ b/DD.Persistence/Services/Interfaces/ITimestampedValuesService.cs @@ -1,4 +1,5 @@ -using DD.Persistence.Models; +using DD.Persistence.Filter.Models.Abstractions; +using DD.Persistence.Models; using DD.Persistence.Models.Common; namespace DD.Persistence.Services.Interfaces; @@ -22,13 +23,14 @@ public interface ITimestampedValuesService /// /// Набор дискриминаторов (идентификаторов) /// + /// /// /// /// /// /// Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, - IEnumerable? columnNames, int skip, int take, CancellationToken token); + TNode? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token); /// /// Получение данных с начала diff --git a/DD.Persistence/Services/TimestampedValuesService.cs b/DD.Persistence/Services/TimestampedValuesService.cs index cef700f..f18bcd9 100644 --- a/DD.Persistence/Services/TimestampedValuesService.cs +++ b/DD.Persistence/Services/TimestampedValuesService.cs @@ -1,4 +1,5 @@ using DD.Persistence.Extensions; +using DD.Persistence.Filter.Models.Abstractions; using DD.Persistence.Models; using DD.Persistence.Repositories; using DD.Persistence.Services.Interfaces; @@ -34,9 +35,9 @@ public class TimestampedValuesService : ITimestampedValuesService } /// - public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable? columnNames, int skip, int take, CancellationToken token) + public async Task> Get(IEnumerable discriminatorIds, DateTimeOffset? geTimestamp, TNode? filterTree, IEnumerable? columnNames, int skip, int take, CancellationToken token) { - var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, columnNames, skip, take, token); + var result = await timestampedValuesRepository.Get(discriminatorIds, geTimestamp, filterTree, columnNames, skip, take, token); var dtos = await BindingToDataScheme(result, token);