Compare commits

..

17 Commits

Author SHA1 Message Date
a5037b8967 Merge pull request '#1003 Убрать ненужные модификаторы virtual' (#29) from VirtualCleaning into master
All checks were successful
Unit tests / tests-and-publication (push) Successful in 2m29s
Reviewed-on: #29
2025-02-20 14:41:44 +05:00
27b9728912 Убрать ненужные модификаторы virtual 2025-02-13 16:53:29 +05:00
93472f7933 Обновить DD.Persistence.API/Readme.md
All checks were successful
Unit tests / tests-and-publication (push) Successful in 1m56s
2025-02-13 13:27:58 +05:00
0d9c61c905 Обновить DD.Persistence.API/Readme.md
Some checks failed
Unit tests / tests-and-publication (push) Has been cancelled
2025-02-13 13:27:06 +05:00
36274b3a5e Обновить DD.Persistence.API/Readme.md
Some checks failed
Unit tests / tests-and-publication (push) Has been cancelled
2025-02-13 13:26:45 +05:00
b30d28dbeb Обновления в файле readme.md
Some checks failed
Unit tests / tests-and-publication (push) Has been cancelled
2025-02-13 13:23:00 +05:00
233850483c UML-диаграмма процесса пакетного редактирования
Some checks failed
Unit tests / tests-and-publication (push) Has been cancelled
2025-02-13 13:22:28 +05:00
ca0b1f0031 Убрана ссылка на удаленный проект с репозиториями из Dockerfile
All checks were successful
Unit tests / tests-and-publication (push) Successful in 5m16s
2025-02-12 16:43:34 +05:00
3cf7cbcc7c Фикс теста
Some checks failed
Unit tests / tests-and-publication (push) Failing after 2m22s
2025-02-12 16:33:42 +05:00
a49a0f567a Merge pull request '#974 Модифицировать метод Get для TimestampedValues по части применения фильтра' (#28) from TimestampedValuesFilter into master
Some checks failed
Unit tests / tests-and-publication (push) Failing after 1m22s
Reviewed-on: #28
Reviewed-by: on.nemtina <on.nemtina@digitaldrilling.ru>
2025-02-12 15:16:09 +05:00
c904c117d8 Правки после ревью #1 2025-02-12 15:15:43 +05:00
0aca5d2d43 Добавить комментарий для клиента 2025-02-11 12:41:55 +05:00
4b9a4b4db7 Merge branch 'master' into TimestampedValuesFilter 2025-02-11 12:37:04 +05:00
e1f84f3091 Перед материализацией вычитываемых сущностей применить построенный фильтр 2025-02-11 12:34:37 +05:00
2fe369d49e Перевести TimestampedValuesRepository под спецификации 2025-02-10 17:25:45 +05:00
8e2c3a3a55 Добавить парсинг дерева в Get-запрос 2025-02-10 09:27:13 +05:00
83c744939b Merge branch 'FilterBuilder' into FilterBinder 2025-02-07 08:33:40 +05:00
62 changed files with 693 additions and 815 deletions

View File

@ -9,7 +9,7 @@ using DD.Persistence.Models.Common;
namespace DD.Persistence.API.Controllers;
[ApiController]
//[Authorize]
[Authorize]
[Route("api/[controller]")]
public class ChangeLogController : ControllerBase, IChangeLogApi
{

View File

@ -12,7 +12,7 @@ namespace DD.Persistence.API.Controllers;
/// Работа с уставками
/// </summary>
[ApiController]
//[Authorize]
[Authorize]
[Route("api/[controller]")]
public class SetpointController : ControllerBase, ISetpointApi
{
@ -105,8 +105,8 @@ public class SetpointController : ControllerBase, ISetpointApi
[ProducesResponseType(typeof(int), (int)HttpStatusCode.Created)]
public async Task<IActionResult> Add(Guid setpointKey, object newValue, CancellationToken token)
{
//var userId = User.GetUserId<Guid>();
await setpointRepository.Add(setpointKey, (JsonElement)newValue, Guid.NewGuid(), token);
var userId = User.GetUserId<Guid>();
await setpointRepository.Add(setpointKey, (JsonElement)newValue, userId, token);
return CreatedAtAction(nameof(Add), true);
}

View File

@ -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
/// </summary>
/// <param name="discriminatorIds">Набор дискриминаторов</param>
/// <param name="timestampBegin">Фильтр позднее даты</param>
/// <param name="filterTree">Кастомный фильтр по набору значений</param>
/// <param name="columnNames">Фильтр свойств набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
@ -52,9 +54,14 @@ public class TimestampedValuesController : ControllerBase
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<TimestampedValuesDto>), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.NoContent)]
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> Get([FromQuery] IEnumerable<Guid> discriminatorIds, DateTimeOffset? timestampBegin, [FromQuery] string[]? columnNames, int skip, int take, CancellationToken token)
public async Task<ActionResult<IEnumerable<TimestampedValuesDto>>> Get([FromQuery] IEnumerable<Guid> 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();
}

View File

@ -28,4 +28,8 @@
<ProjectReference Include="..\DD.Persistence\DD.Persistence.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Docs\" />
</ItemGroup>
</Project>

View File

@ -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<TNode>(() => new OpenApiSchema { Type = "string" });
c.CustomOperationIds(e =>
{

View File

@ -0,0 +1,359 @@
<?xml version="1.0" encoding="UTF-8"?>
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36" version="24.8.3">
<diagram name="Страница — 1" id="7k5Wemfp-yc9piGHxsiE">
<mxGraphModel dx="2049" dy="1054" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1300" pageHeight="1050" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="_dnhcZFeje3u91oS2JwL-1" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="355" y="930" as="sourcePoint" />
<mxPoint x="355" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-2" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="555" y="930" as="sourcePoint" />
<mxPoint x="555" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-4" value="&lt;b&gt;FRONT&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;strokeColor=#432D57;fontColor=#ffffff;" vertex="1" parent="1">
<mxGeometry x="325" y="180" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-5" value="&lt;b&gt;BACK&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="525" y="180" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-7" value="&lt;b&gt;CACHE&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="725" y="180" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-8" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="955" y="930" as="sourcePoint" />
<mxPoint x="955" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-9" value="&lt;b&gt;COMMIT REPOSITORY&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="880" y="180" width="155" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-32" value="" style="endArrow=none;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;align=center;" edge="1" parent="1" target="_dnhcZFeje3u91oS2JwL-7">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="930" as="sourcePoint" />
<mxPoint x="756.5000000000002" y="300" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-52" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="300" as="sourcePoint" />
<mxPoint x="755" y="300" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-53" value="&lt;span style=&quot;font-size: 12px; text-wrap-mode: wrap;&quot;&gt;GetOrCreate(userId, message)&lt;/span&gt;" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-52">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-67" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="310" as="sourcePoint" />
<mxPoint x="955" y="310.83" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-68" value="CreateCommit(userId, message)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-67">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-69" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="955" y="330" as="sourcePoint" />
<mxPoint x="755" y="330" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-70" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-69">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-88" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1155" y="930" as="sourcePoint" />
<mxPoint x="1155" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-89" value="&lt;b&gt;CHANGE LOG REPOSITORY&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#76608a;fontColor=#ffffff;strokeColor=#432D57;" vertex="1" parent="1">
<mxGeometry x="1060" y="180" width="185" height="30" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-90" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="340" as="sourcePoint" />
<mxPoint x="555" y="340" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-91" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-90">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-93" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="380" as="sourcePoint" />
<mxPoint x="1155" y="380" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-94" value="AddRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-93">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-96" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1155" y="410" as="sourcePoint" />
<mxPoint x="555" y="410" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-97" value="CREATED" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-96">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-100" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="420" as="sourcePoint" />
<mxPoint x="355" y="420" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-101" value="CREATED" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-100">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-102" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="555" y="420" as="sourcePoint" />
<mxPoint x="555" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-104" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="290" as="sourcePoint" />
<mxPoint x="555" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-105" value="Insert(items, message) [POST]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-104">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-107" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="354.13" y="420" as="sourcePoint" />
<mxPoint x="354.13" y="290" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-108" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="520" as="sourcePoint" />
<mxPoint x="555" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-109" value="Update(items, message) [PUT]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-108">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-110" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="530" as="sourcePoint" />
<mxPoint x="755" y="530" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-111" value="&lt;span style=&quot;font-size: 12px; text-wrap-mode: wrap;&quot;&gt;GetOrCreate(userId, message)&lt;/span&gt;" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-110">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-112" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="560" as="sourcePoint" />
<mxPoint x="555" y="560" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-113" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-112">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-114" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="554.5699999999999" y="650" as="sourcePoint" />
<mxPoint x="554.5699999999999" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-115" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="610" as="sourcePoint" />
<mxPoint x="1155" y="610" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-116" value="UpdateRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-115">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-117" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1155" y="640" as="sourcePoint" />
<mxPoint x="555" y="640" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-118" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-117">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-119" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="650" as="sourcePoint" />
<mxPoint x="355" y="650" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-120" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-119">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-121" value="" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;curved=0;rounded=0;endSize=8;startSize=8;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="530" as="sourcePoint" />
<mxPoint x="755" y="560" as="targetPoint" />
<Array as="points">
<mxPoint x="775" y="550" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-122" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="760" as="sourcePoint" />
<mxPoint x="555" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-123" value="Delete(items, message) [DELETE]" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-122">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-124" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="770" as="sourcePoint" />
<mxPoint x="755" y="770" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-125" value="&lt;span style=&quot;font-size: 12px; text-wrap-mode: wrap;&quot;&gt;GetOrCreate(userId, message)&lt;/span&gt;" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-124">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-126" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="755" y="800" as="sourcePoint" />
<mxPoint x="555" y="800" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-127" value="CommitId" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-126">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-128" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="554.5699999999999" y="890" as="sourcePoint" />
<mxPoint x="554.5699999999999" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-129" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="850" as="sourcePoint" />
<mxPoint x="1155" y="850" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-130" value="UpdateRange(items, commitId)" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-129">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-131" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1155" y="880" as="sourcePoint" />
<mxPoint x="555" y="880" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-132" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-131">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-133" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="555" y="890" as="sourcePoint" />
<mxPoint x="355" y="890" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-134" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-133">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-135" value="" style="edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;curved=0;rounded=0;endSize=8;startSize=8;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="770" as="sourcePoint" />
<mxPoint x="755" y="800" as="targetPoint" />
<Array as="points">
<mxPoint x="775" y="790" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-136" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="354.57" y="650" as="sourcePoint" />
<mxPoint x="354.57" y="520" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-137" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=9;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="354.57" y="890" as="sourcePoint" />
<mxPoint x="354.57" y="760" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-138" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1155" y="411" as="sourcePoint" />
<mxPoint x="1155" y="381" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-139" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1155" y="641" as="sourcePoint" />
<mxPoint x="1155" y="611" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-140" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#f8cecc;gradientColor=#ea6b66;strokeColor=#b85450;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="1154.75" y="881" as="sourcePoint" />
<mxPoint x="1154.75" y="851" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-141" value="" style="endArrow=none;html=1;rounded=0;strokeWidth=10;curved=1;targetPerimeterSpacing=3;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;opacity=50;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="755" y="830" as="sourcePoint" />
<mxPoint x="754.93" y="312" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-142" value="" style="endArrow=none;html=1;rounded=0;fillColor=#dae8fc;strokeColor=#6c8ebf;strokeWidth=10;opacity=50;gradientColor=#7ea6e0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="165" y="882" as="sourcePoint" />
<mxPoint x="165.22" y="292" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-143" value="&lt;font color=&quot;#330066&quot;&gt;User&lt;/font&gt;" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;fillColor=#76608a;strokeColor=#432D57;fontColor=#ffffff;align=center;" vertex="1" parent="1">
<mxGeometry x="155" y="180" width="21.5" height="43" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-146" value="" style="endArrow=classic;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="170" y="280" as="sourcePoint" />
<mxPoint x="360" y="280" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-147" value="SAVE" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;labelBackgroundColor=none;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-146">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-148" value="" style="endArrow=none;html=1;rounded=0;align=center;" edge="1" parent="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="165.22" y="930" as="sourcePoint" />
<mxPoint x="165.22" y="210" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-149" value="" style="endArrow=classic;html=1;rounded=0;dashed=1;align=center;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="355" y="892" as="sourcePoint" />
<mxPoint x="165" y="892" as="targetPoint" />
<Array as="points">
<mxPoint x="265" y="892" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-150" value="OK" style="edgeLabel;resizable=0;html=1;;align=center;verticalAlign=middle;" connectable="0" vertex="1" parent="_dnhcZFeje3u91oS2JwL-149">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-152" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;UML-диаграмма процесса пакетного редактирования&lt;/h1&gt;&lt;p&gt;.&lt;/p&gt;" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;align=center;" vertex="1" parent="1">
<mxGeometry x="75" y="50" width="1150" height="70" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-154" value="frame" style="shape=umlFrame;whiteSpace=wrap;html=1;pointerEvents=0;" vertex="1" parent="1">
<mxGeometry x="120" y="250" width="1120" height="680" as="geometry" />
</mxCell>
<mxCell id="_dnhcZFeje3u91oS2JwL-156" value="&lt;p style=&quot;line-height: 120%;&quot;&gt;Время жизни кеша задается внутри проекта&lt;/p&gt;" style="shape=note;size=20;whiteSpace=wrap;html=1;labelBackgroundColor=none;fillColor=#d0cee2;strokeColor=#56517e;opacity=50;" vertex="1" parent="1">
<mxGeometry x="760" y="660" width="120" height="100" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View File

@ -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)

View File

@ -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"

View File

@ -1,10 +1,10 @@
{
"DbConnection": {
"Host": "localhost",
"Host": "postgres",
"Port": 5432,
"Database": "persistence",
"Username": "postgres",
"Password": "q"
"Password": "postgres"
},
"NeedUseKeyCloak": false,
"AuthUser": {

View File

@ -6,7 +6,7 @@
}
},
"ConnectionStrings": {
"DefaultConnection": "Host=localhost:5462;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
"DefaultConnection": "Host=localhost;Database=persistence;Username=postgres;Password=postgres;Persist Security Info=True"
},
"AllowedHosts": "*",
"NeedUseKeyCloak": false,

View File

@ -24,12 +24,14 @@ public interface ITimestampedValuesClient : IDisposable
/// </summary>
/// <param name="discriminatorIds">Набор дискриминаторов (идентификаторов)</param>
/// <param name="timestampBegin">Фильтр позднее даты</param>
/// <param name="filterTree">Кастомный фильтр по набору значений</param>
/// <param name="columnNames">Фильтр свойств набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
string? filterTree,
IEnumerable<string>? columnNames,
int skip,
int take,
@ -40,11 +42,12 @@ public interface ITimestampedValuesClient : IDisposable
/// </summary>
/// <param name="discriminatorId"></param>
/// <param name="geTimestamp"></param>
/// <param name="filterTree"></param>
/// <param name="columnNames">Фильтр свойств набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
/// <summary>
/// Получить данные, начиная с заданной отметки времени

View File

@ -23,6 +23,7 @@ public interface IRefitTimestampedValuesClient : IRefitClient, IDisposable
[Get($"{baseUrl}")]
Task<IApiResponse<IEnumerable<TimestampedValuesDto>>> Get([Query(CollectionFormat.Multi)] IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
[Query] string? filterTree,
[Query(CollectionFormat.Multi)] IEnumerable<string>? columnNames,
int skip,
int take,

View File

@ -32,17 +32,17 @@ public class TimestampedValuesClient : BaseClient, ITimestampedValuesClient
}
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? 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!;
}
/// <inheritdoc/>
public async Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
public async Task<IEnumerable<T>> Get<T>(Guid discriminatorId, DateTimeOffset? geTimestamp, string? filterTree, IEnumerable<string>? 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<T>(discriminatorId);
return data.Select(mapper.DeserializeTimeStampedData);

View File

@ -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
{
/// <inheritdoc />
@ -37,14 +37,14 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания записи");
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid>("IdDiscriminator")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");

View File

@ -17,7 +17,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
columns: table => new
{
Id = table.Column<Guid>(type: "uuid", nullable: false, comment: "Ключ записи"),
IdDiscriminator = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
DiscriminatorId = table.Column<Guid>(type: "uuid", nullable: false, comment: "Дискриминатор таблицы"),
IdAuthor = table.Column<Guid>(type: "uuid", nullable: false, comment: "Автор изменения"),
IdEditor = table.Column<Guid>(type: "uuid", nullable: true, comment: "Редактор"),
Creation = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false, comment: "Дата создания записи"),

View File

@ -1,230 +0,0 @@
// <auto-generated />
using System;
using System.Text.Json;
using DD.Persistence.Database.Model;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace DD.Persistence.Database.Postgres.Migrations
{
[DbContext(typeof(PersistencePostgresContext))]
[Migration("20250210104036_IdDiscriminatorUpdateToDiscriminatorId")]
partial class IdDiscriminatorUpdateToDiscriminatorId
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
modelBuilder.Entity("DD.Persistence.Database.Entity.ChangeLog", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Ключ записи");
b.Property<DateTimeOffset>("Creation")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания записи");
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор таблицы");
b.Property<Guid>("IdAuthor")
.HasColumnType("uuid")
.HasComment("Автор изменения");
b.Property<Guid?>("IdEditor")
.HasColumnType("uuid")
.HasComment("Редактор");
b.Property<Guid?>("IdNext")
.HasColumnType("uuid")
.HasComment("Id заменяющей записи");
b.Property<DateTimeOffset?>("Obsolete")
.HasColumnType("timestamp with time zone")
.HasComment("Дата устаревания (например при удалении)");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Значение");
b.HasKey("Id");
b.ToTable("change_log");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.DataSourceSystem", b =>
{
b.Property<Guid>("SystemId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id системы - источника данных");
b.Property<string>("Description")
.HasColumnType("text")
.HasComment("Описание системы - источника данных");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Наименование системы - источника данных");
b.HasKey("SystemId");
b.ToTable("data_source_system");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.ParameterData", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор системы");
b.Property<int>("ParameterId")
.HasColumnType("integer")
.HasComment("Id параметра");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Value")
.IsRequired()
.HasColumnType("varchar(256)")
.HasComment("Значение параметра в виде строки");
b.HasKey("DiscriminatorId", "ParameterId", "Timestamp");
b.ToTable("parameter_data");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.SchemeProperty", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Идентификатор схемы данных");
b.Property<int>("Index")
.HasColumnType("integer")
.HasComment("Индекс поля");
b.Property<byte>("PropertyKind")
.HasColumnType("smallint")
.HasComment("Тип индексируемого поля");
b.Property<string>("PropertyName")
.IsRequired()
.HasColumnType("text")
.HasComment("Наименования индексируемого поля");
b.HasKey("DiscriminatorId", "Index");
b.ToTable("scheme_property");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Ключ");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата создания уставки");
b.Property<Guid>("IdUser")
.HasColumnType("uuid")
.HasComment("Id автора последнего изменения");
b.Property<JsonElement>("Value")
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("setpoint");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.Property<Guid>("EventId")
.ValueGeneratedOnAdd()
.HasColumnType("uuid")
.HasComment("Id события");
b.Property<int>("CategoryId")
.HasColumnType("integer")
.HasComment("Id Категории важности");
b.Property<int>("EventState")
.HasColumnType("integer")
.HasComment("Статус события");
b.Property<Guid>("SystemId")
.HasColumnType("uuid")
.HasComment("Id системы, к которой относится сообщение");
b.Property<string>("Text")
.IsRequired()
.HasColumnType("varchar(512)")
.HasComment("Текст сообщения");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Дата возникновения");
b.HasKey("EventId");
b.HasIndex("SystemId");
b.ToTable("tech_message");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TimestampedValues", b =>
{
b.Property<Guid>("DiscriminatorId")
.HasColumnType("uuid")
.HasComment("Дискриминатор системы");
b.Property<DateTimeOffset>("Timestamp")
.HasColumnType("timestamp with time zone")
.HasComment("Временная отметка");
b.Property<string>("Values")
.IsRequired()
.HasColumnType("jsonb")
.HasComment("Данные");
b.HasKey("DiscriminatorId", "Timestamp");
b.ToTable("timestamped_values");
});
modelBuilder.Entity("DD.Persistence.Database.Entity.TechMessage", b =>
{
b.HasOne("DD.Persistence.Database.Entity.DataSourceSystem", "System")
.WithMany()
.HasForeignKey("SystemId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("System");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -1,38 +0,0 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace DD.Persistence.Database.Postgres.Migrations
{
/// <inheritdoc />
public partial class IdDiscriminatorUpdateToDiscriminatorId : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "Key",
table: "setpoint",
newName: "DiscriminatorId");
migrationBuilder.RenameColumn(
name: "IdDiscriminator",
table: "change_log",
newName: "DiscriminatorId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.RenameColumn(
name: "DiscriminatorId",
table: "setpoint",
newName: "Key");
migrationBuilder.RenameColumn(
name: "DiscriminatorId",
table: "change_log",
newName: "IdDiscriminator");
}
}
}

View File

@ -135,7 +135,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
modelBuilder.Entity("DD.Persistence.Database.Entity.Setpoint", b =>
{
b.Property<Guid>("DiscriminatorId")
b.Property<Guid>("Key")
.HasColumnType("uuid")
.HasComment("Ключ");
@ -151,7 +151,7 @@ namespace DD.Persistence.Database.Postgres.Migrations
.HasColumnType("jsonb")
.HasComment("Значение уставки");
b.HasKey("DiscriminatorId", "Timestamp");
b.HasKey("Key", "Timestamp");
b.ToTable("setpoint");
});

View File

@ -42,6 +42,8 @@ public static class DependencyInjection
MapsterSetup();
//services.AddTransient(typeof(PersistenceRepository<TimestampedValues>));
services.AddTransient<ISetpointRepository, SetpointRepository>();
services.AddTransient<IChangeLogRepository, ChangeLogRepository>();
services.AddTransient<ITimestampedValuesRepository, TimestampedValuesRepository>();

View File

@ -11,7 +11,7 @@ namespace DD.Persistence.Database.Entity;
/// Часть записи, описывающая изменение
/// </summary>
[Table("change_log")]
public class ChangeLog : IChangeLog, IDiscriminatorItem
public class ChangeLog : IDiscriminatorItem, IChangeLog
{
[Key, Comment("Ключ записи")]
public Guid Id { get; set; }

View File

@ -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; }

View File

@ -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; }

View File

@ -6,11 +6,11 @@ using System.Text.Json;
namespace DD.Persistence.Database.Entity;
[Table("setpoint")]
[PrimaryKey(nameof(DiscriminatorId), nameof(Timestamp))]
public class Setpoint : ITimestampedItem, IDiscriminatorItem
[PrimaryKey(nameof(Key), nameof(Timestamp))]
public class Setpoint : ITimestampedItem
{
[Comment("Ключ")]
public Guid DiscriminatorId { get; set; }
public Guid Key { get; set; }
[Column(TypeName = "jsonb"), Comment("Значение уставки")]
public required JsonElement Value { get; set; }

View File

@ -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; }
}

View File

@ -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; }

View File

@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.EntityAbstractions;
public interface IDiscriminatorItem
{
/// <summary>

View File

@ -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<TEntity>? BuildFilter<TEntity>(this DataSchemeDto dataSchemeDto, TNode root)
where TEntity : IValuesItem
public static IQueryable<TEntity> ApplyFilter<TEntity>(this IQueryable<TEntity> query, DataSchemeDto dataSchemeDto, TNode root)
where TEntity : class, IValuesItem
{
var filterSpec = dataSchemeDto.BuildFilter<TEntity>(root);
if (filterSpec != null)
return query.WithSpecification(filterSpec);
return query;
}
private static ISpecification<TEntity>? BuildFilter<TEntity>(this DataSchemeDto dataSchemeDto, TNode root)
where TEntity : IValuesItem
{
var result = dataSchemeDto.BuildSpecificationByNextNode<TEntity>(root);
@ -48,10 +58,10 @@ public static class FilterBuilder
switch (vertex.Operation)
{
case OperationEnum.And:
result = new AndSpecification<TEntity>(leftSpecification, rigthSpecification);
result = new AndSpec<TEntity>(leftSpecification, rigthSpecification);
break;
case OperationEnum.Or:
result = new OrSpecification<TEntity>(leftSpecification, rigthSpecification);
result = new OrSpec<TEntity>(leftSpecification, rigthSpecification);
break;
}
@ -86,21 +96,21 @@ public static class FilterBuilder
private static Dictionary<OperationEnum, Func<int, string?, ISpecification<TEntity>>> StringSpecifications<TEntity>()
where TEntity : IValuesItem => new()
{
{ OperationEnum.Equal, (int index, string? value) => new ValueEqaulSpecification<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, string? value) => new ValueGreateSpecification<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Less, (int index, string? value) => new ValueLessSpecification<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpecification<TEntity>(index, value) }
{ OperationEnum.Equal, (int index, string? value) => new ValueEqualSpec<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, string? value) => new ValueNotEqualSpec<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, string? value) => new ValueGreateSpec<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, string? value) => new ValueGreateOrEqualSpec<TEntity>(index, value) },
{ OperationEnum.Less, (int index, string? value) => new ValueLessSpec<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, string? value) => new ValueLessOrEqualSpec<TEntity>(index, value) }
};
private static Dictionary<OperationEnum, Func<int, double?, ISpecification<TEntity>>> DoubleSpecifications<TEntity>()
where TEntity : IValuesItem => new()
{
{ OperationEnum.Equal, (int index, double? value) => new ValueEqaulSpecification<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, double? value) => new ValueGreateSpecification<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpecification<TEntity>(index, value) },
{ OperationEnum.Less, (int index, double? value) => new ValueLessSpecification<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpecification<TEntity>(index, value) }
{ OperationEnum.Equal, (int index, double? value) => new ValueEqualSpec<TEntity>(index, value) },
{ OperationEnum.NotEqual, (int index, double? value) => new ValueNotEqualSpec<TEntity>(index, value) },
{ OperationEnum.Greate, (int index, double? value) => new ValueGreateSpec<TEntity>(index, value) },
{ OperationEnum.GreateOrEqual, (int index, double? value) => new ValueGreateOrEqualSpec<TEntity>(index, value) },
{ OperationEnum.Less, (int index, double? value) => new ValueLessSpec<TEntity>(index, value) },
{ OperationEnum.LessOrEqual, (int index, double? value) => new ValueLessOrEqualSpec<TEntity>(index, value) }
};
}

View File

@ -1,9 +1,5 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Postgres.Helpers;
using DD.Persistence.Database.Specifications.ChangeLog;
using DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Models.Requests;
@ -16,13 +12,10 @@ namespace DD.Persistence.Database.Repositories;
public class ChangeLogRepository : IChangeLogRepository
{
private readonly DbContext db;
private readonly ObsoleteIsNullSpec<ChangeLog> obsoleteIsNullSpecification;
public ChangeLogRepository(DbContext db)
{
this.db = db;
this.obsoleteIsNullSpecification = new ObsoleteIsNullSpec<ChangeLog>();
}
public async Task<int> AddRange(Guid idAuthor, Guid idDiscriminator, IEnumerable<ChangeLogValuesDto> dtos, CancellationToken token)
@ -42,11 +35,9 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<int> MarkAsDeleted(Guid idEditor, IEnumerable<Guid> ids, CancellationToken token)
{
var containsIdsSpecification = new IdContainsSpec<ChangeLog>(ids);
var query = db.Set<ChangeLog>()
.WithSpecification(containsIdsSpecification)
.WithSpecification(obsoleteIsNullSpecification);
.Where(s => ids.Contains(s.Id))
.Where(s => s.Obsolete == null);
if (query.Count() != ids.Count())
{
@ -62,11 +53,9 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<int> MarkAsDeleted(Guid idEditor, Guid idDiscriminator, CancellationToken token)
{
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual)
.WithSpecification(obsoleteIsNullSpecification);
.Where(s => s.DiscriminatorId == idDiscriminator)
.Where(e => e.Obsolete == null);
var entities = await query.ToArrayAsync(token);
@ -108,10 +97,8 @@ public class ChangeLogRepository : IChangeLogRepository
var dbSet = db.Set<ChangeLog>();
var updatedIds = dtos.Select(d => d.Id);
var containsIdsSpecification = new IdContainsSpec<ChangeLog>(updatedIds);
var updatedEntities = dbSet
.WithSpecification(containsIdsSpecification)
.Where(s => updatedIds.Contains(s.Id))
.ToDictionary(s => s.Id);
var result = 0;
@ -147,11 +134,7 @@ public class ChangeLogRepository : IChangeLogRepository
PaginationRequest paginationRequest,
CancellationToken token)
{
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var query = CreateQuery(idDiscriminator);
query = query.Apply(momentUtc);
var result = await query.ApplyPagination(paginationRequest, Convert, token);
@ -159,21 +142,22 @@ public class ChangeLogRepository : IChangeLogRepository
return result;
}
private IQueryable<ChangeLog> CreateQuery(Guid idDiscriminator)
{
var query = db.Set<ChangeLog>().Where(e => e.DiscriminatorId == idDiscriminator);
return query;
}
public async Task<IEnumerable<ChangeLogDto>> GetChangeLogForInterval(Guid idDiscriminator, DateTimeOffset dateBegin, DateTimeOffset dateEnd, CancellationToken token)
{
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var query = db.Set<ChangeLog>().Where(s => s.DiscriminatorId == idDiscriminator);
var min = new DateTimeOffset(dateBegin.ToUniversalTime().Date, TimeSpan.Zero);
var max = new DateTimeOffset(dateEnd.ToUniversalTime().Date, TimeSpan.Zero);
var specCreatedByDateRange = new FromCreationDateRangeSpec<ChangeLog>(min, max);
var specHistoryByDateRange = new FromObsoleteDateRangeSpece<ChangeLog>(min, max);
var createdQuery = query.WithSpecification(specCreatedByDateRange);
var editedQuery = query.WithSpecification(specHistoryByDateRange);
var createdQuery = query.Where(e => e.Creation >= min && e.Creation <= max);
var editedQuery = query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
query = createdQuery.Union(editedQuery);
var entities = await query.ToArrayAsync(token);
@ -187,11 +171,7 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<DateOnly>> GetDatesChange(Guid idDiscriminator, CancellationToken token)
{
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var specObsoleteNotNull = new ObsoleteNotNullSpec<ChangeLog>();
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual);
var query = db.Set<ChangeLog>().Where(e => e.DiscriminatorId == idDiscriminator);
var datesCreateQuery = query
.Select(e => e.Creation)
@ -200,7 +180,7 @@ public class ChangeLogRepository : IChangeLogRepository
var datesCreate = await datesCreateQuery.ToArrayAsync(token);
var datesUpdateQuery = query
.WithSpecification(specObsoleteNotNull)
.Where(e => e.Obsolete != null)
.Select(e => e.Obsolete!.Value)
.Distinct();
@ -234,13 +214,9 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<IEnumerable<ChangeLogValuesDto>> GetGtDate(Guid idDiscriminator, DateTimeOffset dateBegin, CancellationToken token)
{
var date = dateBegin.ToUniversalTime();
var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
var specCreatedOrUpdatedGeDate = new CreatedOrUpdatedGeDateSpec<ChangeLog>(date);
var query = db.Set<ChangeLog>()
.WithSpecification(specDiscriminatorEqual)
.WithSpecification(specCreatedOrUpdatedGeDate);
.Where(e => e.DiscriminatorId == idDiscriminator)
.Where(e => e.Creation >= date || e.Obsolete >= date);
var entities = await query.ToArrayAsync(token);
@ -251,34 +227,29 @@ public class ChangeLogRepository : IChangeLogRepository
public async Task<DatesRangeDto?> GetDatesRange(Guid idDiscriminator, CancellationToken token)
{
return null;
//var specDiscriminatorEqual = new DiscriminatorEqualSpec<ChangeLog>(idDiscriminator);
//var evaluator =
// SpecificationEvaluator.Default
//var query = db.Set<ChangeLog>()
// //.Where(e => e.IdDiscriminator == idDiscriminator)
// .GroupBy(e => 1)
// .WithSpecification(specDiscriminatorEqual)
// .Select(group => new
// {
// Min = group.Min(e => e.Creation),
// Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation
// ? e.Obsolete.Value
// : e.Creation),
// });
var query = db.Set<ChangeLog>()
.Where(e => e.DiscriminatorId == idDiscriminator)
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Creation),
Max = group.Max(e => e.Obsolete.HasValue && e.Obsolete > e.Creation
? e.Obsolete.Value
: e.Creation),
});
//var values = await query.FirstOrDefaultAsync(token);
var values = await query.FirstOrDefaultAsync(token);
//if (values is null)
//{
// return null;
//}
if (values is null)
{
return null;
}
//return new DatesRangeDto
//{
// From = values.Min,
// To = values.Max,
//};
return new DatesRangeDto
{
From = values.Min,
To = values.Max,
};
}
private ChangeLogValuesDto Convert(ChangeLog entity) => entity.Adapt<ChangeLogValuesDto>();

View File

@ -12,7 +12,7 @@ public class DataSourceSystemRepository : IDataSourceSystemRepository
{
this.db = db;
}
protected virtual IQueryable<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
protected IQueryable<DataSourceSystem> GetQueryReadOnly() => db.Set<DataSourceSystem>();
public virtual async Task Add(DataSourceSystemDto dataSourceSystemDto, CancellationToken token)
{

View File

@ -1,62 +0,0 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.EntityAbstractions;
using DD.Persistence.Models.Common;
namespace DD.Persistence.Database.Postgres.Repositories;
public class MyPartialEvaluator : IEvaluator
{
private MyPartialEvaluator() { }
public static MyPartialEvaluator Instance { get; } = new MyPartialEvaluator();
public bool IsCriteriaEvaluator { get; } = true;
public IQueryable<T> GetQuery<T>(IQueryable<T> query, ISpecification<T> specification) where T : class
{
// Проверяем, есть ли свойство Timestamp в типе T
var timestampProperty = typeof(T).GetProperty("Timestamp");
if (timestampProperty != null && timestampProperty.PropertyType == typeof(DateTimeOffset))
{
// Если свойство существует и имеет тип DateTime, выполняем группировку и выборку
var t = query
.GroupBy(x => 1)
.Select(group => new
{
Min = group.Min(e => (DateTimeOffset)timestampProperty.GetValue(e)),
Max = group.Max(e => (DateTimeOffset)timestampProperty.GetValue(e)),
})
.AsQueryable() as IQueryable<T>;
return t;
}
return query;
}
}
public class Test
{
public Test()
{
}
public DateTimeOffset Min { get; set; }
public DateTimeOffset Max { get; set; }
}
public class MySpecificationEvaluator : SpecificationEvaluator
{
public static MySpecificationEvaluator GroupBy { get; } = new MySpecificationEvaluator();
private MySpecificationEvaluator() : base()
{
Evaluators.Add(MyPartialEvaluator.Instance);
}
}

View File

@ -15,7 +15,7 @@ public class ParameterRepository : IParameterRepository
this.db = db;
}
protected virtual IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
protected IQueryable<ParameterData> GetQueryReadOnly() => db.Set<ParameterData>();
public async Task<DatesRangeDto> GetDatesRangeAsync(Guid idDiscriminator, CancellationToken token)
{

View File

@ -12,7 +12,7 @@ public class SchemePropertyRepository : ISchemePropertyRepository
{
this.db = db;
}
protected virtual IQueryable<SchemeProperty> GetQueryReadOnly() => db.Set<SchemeProperty>();
protected IQueryable<SchemeProperty> GetQueryReadOnly() => db.Set<SchemeProperty>();
public virtual async Task AddRange(DataSchemeDto dataSchemeDto, CancellationToken token)
{

View File

@ -1,8 +1,4 @@
using Ardalis.Specification;
using Ardalis.Specification.EntityFrameworkCore;
using DD.Persistence.Database.Entity;
using DD.Persistence.Database.Specifications.Common;
using DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
using DD.Persistence.Models;
using DD.Persistence.Models.Common;
using DD.Persistence.Repositories;
@ -20,7 +16,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
this.db = db;
}
protected virtual IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
protected IQueryable<Setpoint> GetQueryReadOnly() => db.Set<Setpoint>();
public async Task<IEnumerable<SetpointValueDto>> GetCurrent(
IEnumerable<Guid> setpointKeys,
@ -28,11 +24,9 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var query = GetQueryReadOnly();
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.GroupBy(e => e.DiscriminatorId)
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.GroupBy(e => e.Key)
.Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToArrayAsync(token);
@ -43,27 +37,23 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var query = GetQueryReadOnly();
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
.GroupBy(e => e.DiscriminatorId)
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.GroupBy(e => e.Key)
.Select(g => g.OrderByDescending(x => x.Timestamp).FirstOrDefault())
.ToDictionaryAsync(x=> x.DiscriminatorId, x => (object)x.Value, token);
.ToDictionaryAsync(x=> x.Key, x => (object)x.Value, token);
return entities;
}
public async Task<IEnumerable<SetpointValueDto>> GetHistory(IEnumerable<Guid> setpointKeys, DateTimeOffset historyMoment, CancellationToken token)
{
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.ToArrayAsync(token);
var filteredEntities = entities
.GroupBy(e => e.DiscriminatorId)
.GroupBy(e => e.Key)
.Select(e => e.OrderBy(o => o.Timestamp))
.Select(e => e.Where(e => e.Timestamp <= historyMoment).Last());
var dtos = filteredEntities
@ -87,35 +77,31 @@ namespace DD.Persistence.Database.Postgres.Repositories
public async Task<DatesRangeDto> GetDatesRangeAsync(CancellationToken token)
{
var spec = new DatesRangeSpec<Setpoint>();
var query = db.Set<Setpoint>()
.WithSpecification(spec, MySpecificationEvaluator.GroupBy);
var query2 = GetQueryReadOnly()
var query = GetQueryReadOnly()
.GroupBy(e => 1)
.Select(group => new
{
Min = group.Min(e => e.Timestamp),
Max = group.Max(e => e.Timestamp),
});
var values = await query.FirstOrDefaultAsync(token);
var result = new DatesRangeDto()
{
From = values?.Min ?? DateTimeOffset.MinValue,
To = values?.Max ?? DateTimeOffset.MaxValue
};
return null;
return result;
}
public async Task<Dictionary<Guid, IEnumerable<SetpointLogDto>>> GetLog(IEnumerable<Guid> setpointKeys, CancellationToken token)
{
var specDiscriminatorContains = new DiscriminatorContainsSpec<Setpoint>(setpointKeys);
var entities = await db.Set<Setpoint>()
.WithSpecification(specDiscriminatorContains)
var query = GetQueryReadOnly();
var entities = await query
.Where(e => setpointKeys.Contains(e.Key))
.ToArrayAsync(token);
var dtos = entities
.GroupBy(e => e.DiscriminatorId)
.GroupBy(e => e.Key)
.ToDictionary(e => e.Key, v => v.Select(z => z.Adapt<SetpointLogDto>()));
return dtos;
@ -125,7 +111,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
{
var entity = new Setpoint()
{
DiscriminatorId = setpointKey,
Key = setpointKey,
Value = newValue,
IdUser = idUser,
Timestamp = DateTimeOffset.UtcNow.ToUniversalTime()

View File

@ -20,7 +20,7 @@ namespace DD.Persistence.Database.Postgres.Repositories
this.sourceSystemRepository = sourceSystemRepository;
}
protected virtual IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
protected IQueryable<TechMessage> GetQueryReadOnly() => db.Set<TechMessage>()
.Include(e => e.System);
public async Task<PaginationContainer<TechMessageDto>> GetPage(PaginationRequest request, CancellationToken token)

View File

@ -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,15 +10,16 @@ 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<TimestampedValues> GetQueryReadOnly() => this.db.Set<TimestampedValues>();
protected IQueryable<TimestampedValues> GetQueryReadOnly() => db.Set<TimestampedValues>();
public async virtual Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
public async Task<int> AddRange(Guid discriminatorId, IEnumerable<TimestampedValuesDto> dtos, CancellationToken token)
{
var timestampedValuesEntities = dtos.Select(dto => new TimestampedValues()
{
@ -24,36 +27,47 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
Timestamp = dto.Timestamp.ToUniversalTime(),
Values = dto.Values.Values.ToArray()
});
await db.Set<TimestampedValues>().AddRangeAsync(timestampedValuesEntities, token);
await db.AddRangeAsync(timestampedValuesEntities, token);
var result = await db.SaveChangesAsync(token);
return result;
}
public async virtual Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> discriminatorIds,
DateTimeOffset? timestampBegin,
public async Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> discriminatorIds,
DateTimeOffset? geTimestamp,
TNode? filterTree,
IEnumerable<string>? columnNames,
int skip,
int take,
CancellationToken token)
{
var query = GetQueryReadOnly()
.Where(entity => discriminatorIds.Contains(entity.DiscriminatorId));
// Фильтрация по дате
if (timestampBegin.HasValue)
var resultQuery = Array.Empty<TimestampedValues>().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
@ -62,7 +76,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetFirst(Guid discriminatorId, int takeCount, CancellationToken token)
{
var query = GetQueryReadOnly()
.OrderBy(e => e.Timestamp)
@ -77,7 +91,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetLast(Guid discriminatorId, int takeCount, CancellationToken token)
{
var query = GetQueryReadOnly()
.OrderByDescending(e => e.Timestamp)
@ -93,7 +107,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
}
// ToDo: прореживание должно осуществляться до материализации
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetResampledData(
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetResampledData(
Guid discriminatorId,
DateTimeOffset dateBegin,
double intervalSec = 600d,
@ -114,10 +128,11 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async virtual Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> GetGtDate(Guid discriminatorId, DateTimeOffset timestampBegin, CancellationToken token)
public async Task<IEnumerable<(DateTimeOffset Timestamp, object[] Values)>> 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 => (
@ -128,7 +143,7 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return result;
}
public async virtual Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
public async Task<DatesRangeDto?> GetDatesRange(Guid discriminatorId, CancellationToken token)
{
var query = GetQueryReadOnly()
.GroupBy(entity => entity.DiscriminatorId)
@ -153,26 +168,12 @@ public class TimestampedValuesRepository : ITimestampedValuesRepository
return dto;
}
public virtual Task<int> Count(Guid discriminatorId, CancellationToken token)
public async Task<int> Count(Guid discriminatorId, CancellationToken token)
{
var dbSet = db.Set<TimestampedValues>();
var query = dbSet.Where(entity => entity.DiscriminatorId == discriminatorId);
var query = GetQueryReadOnly()
.Where(e => e.DiscriminatorId == discriminatorId);
return query.CountAsync(token);
}
/// <summary>
/// Применить фильтр по дате
/// </summary>
/// <param name="query"></param>
/// <param name="timestampBegin"></param>
/// <returns></returns>
private IQueryable<TimestampedValues> ApplyGeTimestamp(IQueryable<TimestampedValues> query, DateTimeOffset timestampBegin)
{
var geTimestampUtc = timestampBegin.ToUniversalTime();
var result = query
.Where(entity => entity.Timestamp >= geTimestampUtc);
var result = await query.CountAsync(token);
return result;
}

View File

@ -1,17 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DD.Persistence.Database.Specifications.ChangeLog;
public class CreatedOrUpdatedGeDateSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public CreatedOrUpdatedGeDateSpec(DateTimeOffset date)
{
Query.Where(e => e.Creation >= date || e.Obsolete >= date);
}
}

View File

@ -1,25 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска созданных за определённый период записей IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class FromCreationDateRangeSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public FromCreationDateRangeSpec(DateTimeOffset? min, DateTimeOffset? max)
{
if (min.HasValue)
{
Query.Where(e => e.Creation >= min);
}
if (max.HasValue){
Query.Where(e => e.Creation <= max);
}
//Query.Where(e => e.Creation >= min && e.Creation <= max);
}
}

View File

@ -1,18 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска исторических записей IChangeLog за определённый период
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class FromObsoleteDateRangeSpece<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public FromObsoleteDateRangeSpece(DateTimeOffset min, DateTimeOffset max)
{
Query.Where(e => e.Obsolete != null && e.Obsolete >= min && e.Obsolete <= max);
}
}

View File

@ -1,22 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска записей IChangeLog по массиву ключей
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class IdContainsSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public IdContainsSpec()
{
}
public IdContainsSpec(IEnumerable<Guid> ids)
{
Query.Where(s => ids.Contains(s.Id));
}
}

View File

@ -1,18 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для актуальных значений IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ObsoleteIsNullSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public ObsoleteIsNullSpec()
{
Query.Where(e => e.Obsolete == null);
}
}

View File

@ -1,18 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
namespace DD.Persistence.Database.Specifications.ChangeLog;
/// <summary>
/// Спецификация для поиска не актуальных значений IChangeLog
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ObsoleteNotNullSpec<TEntity> : Specification<TEntity>
where TEntity : IChangeLog
{
public ObsoleteNotNullSpec()
{
Query.Where(e => e.Obsolete != null);
}
}

View File

@ -1,19 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common;
public class DatesRangeSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DatesRangeSpec()
{
//Query;
}
}

View File

@ -1,18 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
public class DiscriminatorContainsSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DiscriminatorContainsSpec(IEnumerable<Guid> discriminatorIds)
{
Query.Where(e => discriminatorIds.Contains(e.DiscriminatorId));
}
}

View File

@ -1,18 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.EntityAbstractions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory;
namespace DD.Persistence.Database.Specifications.Common.DiscriminatorItem;
public class DiscriminatorEqualSpec<TEntity> : Specification<TEntity>
where TEntity : IDiscriminatorItem
{
public DiscriminatorEqualSpec(Guid discriminatorId)
{
Query.Where(e => e.DiscriminatorId == discriminatorId);
}
}

View File

@ -1,9 +1,9 @@
using Ardalis.Specification;
namespace DD.Persistence.Database.Specifications;
public class AndSpecification<TEntity> : Specification<TEntity>
namespace DD.Persistence.Database.Specifications.Operation;
public class AndSpec<TEntity> : Specification<TEntity>
{
public AndSpecification(ISpecification<TEntity> first, ISpecification<TEntity> second)
public AndSpec(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
if (first is null || second is null)
return;

View File

@ -0,0 +1,15 @@
using Ardalis.Specification;
using DD.Persistence.Database.Postgres.Extensions;
namespace DD.Persistence.Database.Specifications.Operation;
public class OrSpec<TEntity> : Specification<TEntity>
{
public OrSpec(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
var orExpression = first.Or(second);
if (orExpression == null)
return;
Query.Where(orExpression);
}
}

View File

@ -1,15 +0,0 @@
using Ardalis.Specification;
using DD.Persistence.Database.Postgres.Extensions;
namespace DD.Persistence.Database.Specifications;
public class OrSpecification<TEntity> : Specification<TEntity>
{
public OrSpecification(ISpecification<TEntity> first, ISpecification<TEntity> second)
{
var orExpression = first.Or(second);
if (orExpression == null)
return;
Query.Where(orExpression);
}
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация эквивалентности значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueEqaulSpecification<TEntity> : Specification<TEntity>
public class ValueEqualSpec<TEntity> : Specification<TEntity>
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);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "больше либо равно" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueGreateOrEqualSpecification<TEntity> : Specification<TEntity>
public class ValueGreateOrEqualSpec<TEntity> : Specification<TEntity>
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);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "больше" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueGreateSpecification<TEntity> : Specification<TEntity>
public class ValueGreateSpec<TEntity> : Specification<TEntity>
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);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "меньше либо равно" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueLessOrEqualSpecification<TEntity> : Specification<TEntity>
public class ValueLessOrEqualSpec<TEntity> : Specification<TEntity>
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);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация "меньше" для значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueLessSpecification<TEntity> : Specification<TEntity>
public class ValueLessSpec<TEntity> : Specification<TEntity>
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);
}

View File

@ -7,15 +7,15 @@ namespace DD.Persistence.Database.Specifications.ValuesItem;
/// Спецификация неравенства значений IValuesItem в соответствии с индексацией
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public class ValueNotEqualSpecification<TEntity> : Specification<TEntity>
public class ValueNotEqualSpec<TEntity> : Specification<TEntity>
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);
}

View File

@ -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<string>() { "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;
}

View File

@ -80,13 +80,10 @@ public class FilterBuilderShould
.AsQueryable();
//act
var specification = dataScheme.BuildFilter<TimestampedValues>(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<TimestampedValues>(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<TimestampedValues>(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);

View File

@ -9,7 +9,7 @@ public class TimestampedValuesServiceShould
{
private readonly ITimestampedValuesRepository timestampedValuesRepository = Substitute.For<ITimestampedValuesRepository>();
private readonly ISchemePropertyRepository dataSchemeRepository = Substitute.For<ISchemePropertyRepository>();
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);
}

View File

@ -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")
));

View File

@ -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;
/// <summary>
/// Абстрактная модель вершины
/// </summary>
public abstract class TNode
public abstract class TNode : IParsable<TNode?>
{
/// <inheritdoc/>
public TNode(OperationEnum operation)
@ -18,6 +21,30 @@ public abstract class TNode
/// </summary>
public OperationEnum Operation { get; }
/// <inheritdoc/>
public static TNode? Parse(string s, IFormatProvider? provider)
{
var result = s.BuildTree();
return result;
}
/// <inheritdoc/>
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;
}
/// <summary>
/// Принять посетителя
/// </summary>

View File

@ -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('\"');

View File

@ -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
/// </summary>
/// <param name="idDiscriminators">Набор дискриминаторов (идентификаторов)</param>
/// <param name="geTimestamp">Фильтр позднее даты</param>
/// <param name="filterTree"></param>
/// <param name="columnNames">Фильтр свойств набора. Можно запросить только некоторые свойства из набора</param>
/// <param name="skip"></param>
/// <param name="take"></param>
@ -37,6 +39,7 @@ public interface ITimestampedValuesRepository : ISyncRepository, ITimeSeriesBase
/// <returns></returns>
Task<IDictionary<Guid, IEnumerable<(DateTimeOffset Timestamp, object[] Values)>>> Get(IEnumerable<Guid> idDiscriminators,
DateTimeOffset? geTimestamp,
TNode? filterTree,
IEnumerable<string>? columnNames,
int skip,
int take,

View File

@ -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
/// </summary>
/// <param name="discriminatorIds">Набор дискриминаторов (идентификаторов)</param>
/// <param name="geTimestamp"></param>
/// <param name="filterTree"></param>
/// <param name="columnNames"></param>
/// <param name="skip"></param>
/// <param name="take"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp,
IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
TNode? filterTree, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token);
/// <summary>
/// Получение данных с начала

View File

@ -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
}
/// <inheritdoc/>
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, IEnumerable<string>? columnNames, int skip, int take, CancellationToken token)
public async Task<IEnumerable<TimestampedValuesDto>> Get(IEnumerable<Guid> discriminatorIds, DateTimeOffset? geTimestamp, TNode? filterTree, IEnumerable<string>? 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);