diff --git a/Directory.Packages.props b/Directory.Packages.props index 95bebf3b..89311431 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -9,6 +9,7 @@ 8.4.0 8.0.1 0.0.3 + 8.0.1-preview.8.24267.1 @@ -22,8 +23,11 @@ + + + @@ -45,6 +49,8 @@ + + diff --git a/src/RookieShop.ApiService/Endpoints/Products/Search.Request.cs b/src/RookieShop.ApiService/Endpoints/Products/Search.Request.cs new file mode 100644 index 00000000..828ea52f --- /dev/null +++ b/src/RookieShop.ApiService/Endpoints/Products/Search.Request.cs @@ -0,0 +1,6 @@ +namespace RookieShop.ApiService.Endpoints.Products; + +public sealed record SearchProductRequest( + string Context, + int PageIndex, + int PageSize); \ No newline at end of file diff --git a/src/RookieShop.ApiService/Endpoints/Products/Search.Response.cs b/src/RookieShop.ApiService/Endpoints/Products/Search.Response.cs new file mode 100644 index 00000000..e5e3541c --- /dev/null +++ b/src/RookieShop.ApiService/Endpoints/Products/Search.Response.cs @@ -0,0 +1,10 @@ +using Ardalis.Result; +using RookieShop.ApiService.ViewModels.Products; + +namespace RookieShop.ApiService.Endpoints.Products; + +public sealed class SearchProductResponse +{ + public PagedInfo? PagedInfo { get; set; } + public List? Products { get; set; } = []; +} \ No newline at end of file diff --git a/src/RookieShop.ApiService/Endpoints/Products/Search.cs b/src/RookieShop.ApiService/Endpoints/Products/Search.cs new file mode 100644 index 00000000..655ce4b0 --- /dev/null +++ b/src/RookieShop.ApiService/Endpoints/Products/Search.cs @@ -0,0 +1,43 @@ +using MediatR; +using Microsoft.AspNetCore.Http.HttpResults; +using RookieShop.ApiService.ViewModels.Products; +using RookieShop.Application.Products.Queries.Search; +using RookieShop.Infrastructure.Endpoints.Abstractions; +using RookieShop.Infrastructure.RateLimiter; + +namespace RookieShop.ApiService.Endpoints.Products; + +public sealed class Search(ISender sender) : IEndpoint, SearchProductRequest> +{ + public void MapEndpoint(IEndpointRouteBuilder app) => + app.MapGet("/products/search", + async ( + string context, + int pageNumber = 1, + int pageSize = 0) => + await HandleAsync(new(context, pageNumber, pageSize))) + .Produces>() + .WithTags(nameof(Products)) + .WithName("Search Products") + .MapToApiVersion(new(1, 0)) + .RequirePerIpRateLimit(); + + public async Task> HandleAsync(SearchProductRequest request, + CancellationToken cancellationToken = default) + { + SearchProductQuery query = new( + request.Context, + request.PageIndex, + request.PageSize); + + var result = await sender.Send(query, cancellationToken); + + SearchProductResponse response = new() + { + PagedInfo = result.PagedInfo, + Products = result.Value.ToProductVm() + }; + + return TypedResults.Ok(response); + } +} \ No newline at end of file diff --git a/src/RookieShop.ApiService/Program.cs b/src/RookieShop.ApiService/Program.cs index c05244ec..898a575e 100644 --- a/src/RookieShop.ApiService/Program.cs +++ b/src/RookieShop.ApiService/Program.cs @@ -72,13 +72,14 @@ } else { - app.UseHttpsRedirection(); app.UseExceptionHandler("/error"); app.UseHsts(); } app.UseAntiforgery(); +app.UseHttpsRedirection(); + app.UseResponseCompression(); app.MapInfrastructure(); diff --git a/src/RookieShop.ApiService/appsettings.Development.json b/src/RookieShop.ApiService/appsettings.Development.json index e63facc8..5ac32f71 100644 --- a/src/RookieShop.ApiService/appsettings.Development.json +++ b/src/RookieShop.ApiService/appsettings.Development.json @@ -18,4 +18,4 @@ "Port": 587, "Email": "nguyenxuannhan.dev@gmail.com" } -} +} \ No newline at end of file diff --git a/src/RookieShop.AppHost/Program.cs b/src/RookieShop.AppHost/Program.cs index 4048c641..310c5941 100644 --- a/src/RookieShop.AppHost/Program.cs +++ b/src/RookieShop.AppHost/Program.cs @@ -22,6 +22,8 @@ var db = builder .AddPostgres("db", postgresUser, postgresPassword, 5432) .WithDataBindMount("../../mnt/postgres") + .WithImage("ankane/pgvector") + .WithImageTag("latest") .WithPgAdmin(); var shopDb = db.AddDatabase("shopdb"); var userDb = db.AddDatabase("userdb"); @@ -40,6 +42,11 @@ var blobs = storage.AddBlobs("blobs"); +// OpenAI +const string openAiName = "openai"; +const string textEmbeddingName = "text-embedding-3-small"; +var openAi = builder.AddConnectionString(openAiName); + // Services and applications var identityService = builder .AddProject("identity-service") @@ -52,7 +59,9 @@ .AddProject("api-service") .WithReference(redis) .WithReference(shopDb) + .WithReference(openAi) .WithEnvironment("SmtpSettings__Secret", emailSecret) + .WithEnvironment("AIOptions__OpenAI__EmbeddingName", textEmbeddingName) .WithEnvironment("AzuriteSettings__ConnectionString", blobs.WithEndpoint()) .WithEnvironment("OpenIdSettings__Authority", identityService.GetEndpoint(protocol)); diff --git a/src/RookieShop.AppHost/RookieShop.AppHost.csproj b/src/RookieShop.AppHost/RookieShop.AppHost.csproj index 63383e2c..beb01ab5 100644 --- a/src/RookieShop.AppHost/RookieShop.AppHost.csproj +++ b/src/RookieShop.AppHost/RookieShop.AppHost.csproj @@ -32,8 +32,10 @@ - - + + \ No newline at end of file diff --git a/src/RookieShop.Application/Extension.cs b/src/RookieShop.Application/Extension.cs index 5a87a385..08b8dfdd 100644 --- a/src/RookieShop.Application/Extension.cs +++ b/src/RookieShop.Application/Extension.cs @@ -7,7 +7,6 @@ using RookieShop.Application.Products.Workers; using RookieShop.Infrastructure.Cache; using RookieShop.Infrastructure.Logging; -using RookieShop.Infrastructure.Metrics; using RookieShop.Infrastructure.Validator; using RookieShop.Persistence; @@ -38,7 +37,6 @@ public static IHostApplicationBuilder AddApplication(this IHostApplicationBuilde cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(TxBehavior<,>), ServiceLifetime.Scoped); cfg.AddOpenBehavior(typeof(QueryCachingBehavior<,>)); - cfg.AddOpenBehavior(typeof(MetricsBehavior<,>)); }); builder.Services.AddHostedService(); diff --git a/src/RookieShop.Application/Products/Commands/Create/CreateProductHandler.cs b/src/RookieShop.Application/Products/Commands/Create/CreateProductHandler.cs index b7b25052..98cee455 100644 --- a/src/RookieShop.Application/Products/Commands/Create/CreateProductHandler.cs +++ b/src/RookieShop.Application/Products/Commands/Create/CreateProductHandler.cs @@ -5,6 +5,7 @@ using RookieShop.Domain.Entities.ProductAggregator; using RookieShop.Domain.Entities.ProductAggregator.Primitives; using RookieShop.Domain.SharedKernel; +using RookieShop.Infrastructure.Ai.Embedded; using RookieShop.Infrastructure.Storage.Azurite; namespace RookieShop.Application.Products.Commands.Create; @@ -12,6 +13,7 @@ namespace RookieShop.Application.Products.Commands.Create; public sealed class CreateProductHandler( IRepository repository, IAzuriteService azuriteService, + IAiService aiService, ILogger logger) : ICommandHandler> { public async Task> Handle(CreateProductCommand request, CancellationToken cancellationToken) @@ -30,6 +32,8 @@ public async Task> Handle(CreateProductCommand request, Cancel logger.LogInformation("[{Command}] - Creating product {@Product}", nameof(CreateProductCommand), JsonSerializer.Serialize(product)); + product.Embedding = await aiService.GetEmbeddingAsync($"{product.Name} {product.Description}", cancellationToken); + var result = await repository.AddAsync(product, cancellationToken); return result.Id; diff --git a/src/RookieShop.Application/Products/Queries/Search/SearchProductHandler.cs b/src/RookieShop.Application/Products/Queries/Search/SearchProductHandler.cs new file mode 100644 index 00000000..6d83b223 --- /dev/null +++ b/src/RookieShop.Application/Products/Queries/Search/SearchProductHandler.cs @@ -0,0 +1,35 @@ +using Ardalis.Result; +using RookieShop.Application.Products.DTOs; +using RookieShop.Domain.Entities.ProductAggregator; +using RookieShop.Domain.Entities.ProductAggregator.Specifications; +using RookieShop.Domain.SharedKernel; +using RookieShop.Infrastructure.Ai.Embedded; +using RookieShop.Infrastructure.Storage.Azurite; + +namespace RookieShop.Application.Products.Queries.Search; + +public sealed class SearchProductHandler( + IAiService aiService, + IReadRepository repository, + IAzuriteService azuriteService) : IQueryHandler>> +{ + public async Task>> Handle(SearchProductQuery request, + CancellationToken cancellationToken) + { + var vector = await aiService.GetEmbeddingAsync(request.Context, cancellationToken); + + ProductsFilterSpec spec = new(vector, request.PageIndex, request.PageSize); + + var products = await repository.ListAsync(spec, cancellationToken); + + products.ForEach(p => p.ImageName = azuriteService.GetFileUrl(p.ImageName)); + + var totalRecords = await repository.CountAsync(spec, cancellationToken); + + var totalPages = (int)Math.Ceiling(totalRecords / (double)request.PageSize); + + PagedInfo pagedInfo = new(request.PageIndex, request.PageSize, totalPages, totalRecords); + + return new(pagedInfo, products.ToProductDto()); + } +} \ No newline at end of file diff --git a/src/RookieShop.Application/Products/Queries/Search/SearchProductQuery.cs b/src/RookieShop.Application/Products/Queries/Search/SearchProductQuery.cs new file mode 100644 index 00000000..11d4f7b6 --- /dev/null +++ b/src/RookieShop.Application/Products/Queries/Search/SearchProductQuery.cs @@ -0,0 +1,10 @@ +using Ardalis.Result; +using RookieShop.Application.Products.DTOs; +using RookieShop.Domain.SharedKernel; + +namespace RookieShop.Application.Products.Queries.Search; + +public sealed record SearchProductQuery( + string Context, + int PageIndex, + int PageSize) : IQuery>>; \ No newline at end of file diff --git a/src/RookieShop.Application/Products/Queries/Search/SearchProductValidator.cs b/src/RookieShop.Application/Products/Queries/Search/SearchProductValidator.cs new file mode 100644 index 00000000..85063681 --- /dev/null +++ b/src/RookieShop.Application/Products/Queries/Search/SearchProductValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace RookieShop.Application.Products.Queries.Search; + +public sealed class SearchProductValidator : AbstractValidator +{ + public SearchProductValidator() + { + RuleFor(x => x.Context).NotEmpty(); + RuleFor(x => x.PageIndex).GreaterThanOrEqualTo(1); + RuleFor(x => x.PageSize).GreaterThanOrEqualTo(0); + } +} \ No newline at end of file diff --git a/src/RookieShop.Domain/Entities/CategoryAggregator/Category.cs b/src/RookieShop.Domain/Entities/CategoryAggregator/Category.cs index 2625f26d..2cac12e1 100644 --- a/src/RookieShop.Domain/Entities/CategoryAggregator/Category.cs +++ b/src/RookieShop.Domain/Entities/CategoryAggregator/Category.cs @@ -5,7 +5,7 @@ namespace RookieShop.Domain.Entities.CategoryAggregator; -public sealed class Category : EntityBase, IAggregateRoot +public sealed class Category : EntityBase, ISoftDelete, IAggregateRoot { /// /// EF mapping constructor @@ -23,6 +23,7 @@ public Category(string title, string? description) public CategoryId Id { get; set; } = new(Guid.NewGuid()); public string Name { get; set; } = string.Empty; public string? Description { get; set; } + public bool IsDeleted { get; set; } public ICollection? Products { get; set; } = []; public void Update(string name, string? description) diff --git a/src/RookieShop.Domain/Entities/ProductAggregator/Product.cs b/src/RookieShop.Domain/Entities/ProductAggregator/Product.cs index eb103270..f1b2360e 100644 --- a/src/RookieShop.Domain/Entities/ProductAggregator/Product.cs +++ b/src/RookieShop.Domain/Entities/ProductAggregator/Product.cs @@ -1,4 +1,6 @@ -using Ardalis.GuardClauses; +using Pgvector; +using System.Text.Json.Serialization; +using Ardalis.GuardClauses; using RookieShop.Domain.Entities.CategoryAggregator; using RookieShop.Domain.Entities.CategoryAggregator.Primitives; using RookieShop.Domain.Entities.FeedbackAggregator; @@ -41,6 +43,7 @@ public Product() public double AverageRating { get; set; } public int TotalReviews { get; set; } public CategoryId? CategoryId { get; set; } + [JsonIgnore] public Vector Embedding { get; set; } = default!; public Category? Category { get; set; } public ICollection? OrderDetails { get; set; } = []; public ICollection? Feedbacks { get; set; } = []; diff --git a/src/RookieShop.Domain/Entities/ProductAggregator/Specifications/ProductsFilterSpec.cs b/src/RookieShop.Domain/Entities/ProductAggregator/Specifications/ProductsFilterSpec.cs index 1353474d..58d09ba2 100644 --- a/src/RookieShop.Domain/Entities/ProductAggregator/Specifications/ProductsFilterSpec.cs +++ b/src/RookieShop.Domain/Entities/ProductAggregator/Specifications/ProductsFilterSpec.cs @@ -1,4 +1,6 @@ using Ardalis.Specification; +using Pgvector; +using Pgvector.EntityFrameworkCore; using RookieShop.Domain.Entities.CategoryAggregator.Primitives; namespace RookieShop.Domain.Entities.ProductAggregator.Specifications; @@ -23,4 +25,9 @@ public sealed class ProductsFilterSpec : Specification } public ProductsFilterSpec() => Query.Where(product => !product.IsDeleted); + + public ProductsFilterSpec(Vector vector, int pageIndex, int pageSize) => + Query.Where(product => !product.IsDeleted) + .OrderBy(c => c.Embedding.CosineDistance(vector)) + .ApplyPaging(pageIndex, pageSize); } \ No newline at end of file diff --git a/src/RookieShop.Domain/RookieShop.Domain.csproj b/src/RookieShop.Domain/RookieShop.Domain.csproj index 4f4b182a..a1683ffd 100644 --- a/src/RookieShop.Domain/RookieShop.Domain.csproj +++ b/src/RookieShop.Domain/RookieShop.Domain.csproj @@ -11,6 +11,11 @@ + + + + + all diff --git a/src/RookieShop.IdentityService/SeedData.cs b/src/RookieShop.IdentityService/SeedData.cs index 4541d3a4..273ea878 100644 --- a/src/RookieShop.IdentityService/SeedData.cs +++ b/src/RookieShop.IdentityService/SeedData.cs @@ -19,19 +19,19 @@ public static void EnsureSeedData(WebApplication app) var roleMgr = scope.ServiceProvider.GetRequiredService>(); - var admin = roleMgr.FindByNameAsync("admin").Result; + var staff = roleMgr.FindByNameAsync("staff").Result; - if (admin is null) + if (staff is null) { - admin = new("admin"); + staff = new("admin"); - var result = roleMgr.CreateAsync(admin).Result; + var result = roleMgr.CreateAsync(staff).Result; if (!result.Succeeded) throw new SeedException(result.Errors.First().Description); - roleMgr.AddClaimAsync(admin, new(JwtClaimTypes.Role, AuthScope.Read)).Wait(); - roleMgr.AddClaimAsync(admin, new(JwtClaimTypes.Role, AuthScope.Write)).Wait(); - roleMgr.AddClaimAsync(admin, new(JwtClaimTypes.Role, AuthScope.All)).Wait(); + roleMgr.AddClaimAsync(staff, new(JwtClaimTypes.Role, AuthScope.Read)).Wait(); + roleMgr.AddClaimAsync(staff, new(JwtClaimTypes.Role, AuthScope.Write)).Wait(); + roleMgr.AddClaimAsync(staff, new(JwtClaimTypes.Role, AuthScope.All)).Wait(); Log.Debug("admin created"); } @@ -40,26 +40,6 @@ public static void EnsureSeedData(WebApplication app) Log.Debug("admin already exists"); } - var user = roleMgr.FindByNameAsync("user").Result; - - if (user is null) - { - user = new("user"); - - var result = roleMgr.CreateAsync(user).Result; - - if (!result.Succeeded) throw new SeedException(result.Errors.First().Description); - - roleMgr.AddClaimAsync(user, new(JwtClaimTypes.Role, AuthScope.Read)).Wait(); - roleMgr.AddClaimAsync(user, new(JwtClaimTypes.Role, AuthScope.Write)).Wait(); - - Log.Debug("user created"); - } - else - { - Log.Debug("user already exists"); - } - var userMgr = scope.ServiceProvider.GetRequiredService>(); var nhan = userMgr.FindByNameAsync("nhan").Result; @@ -122,8 +102,6 @@ public static void EnsureSeedData(WebApplication app) if (!result.Succeeded) throw new SeedException(result.Errors.First().Description); - userMgr.AddToRoleAsync(fox, "user").Wait(); - Log.Debug("fox created"); } else diff --git a/src/RookieShop.Infrastructure/Ai/Embedded/IAiService.cs b/src/RookieShop.Infrastructure/Ai/Embedded/IAiService.cs new file mode 100644 index 00000000..cb61bd45 --- /dev/null +++ b/src/RookieShop.Infrastructure/Ai/Embedded/IAiService.cs @@ -0,0 +1,10 @@ +using Pgvector; + +namespace RookieShop.Infrastructure.Ai.Embedded; + +public interface IAiService +{ + ValueTask GetEmbeddingAsync(string text, CancellationToken cancellationToken = default); + + ValueTask> GetEmbeddingsAsync(List text, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/RookieShop.Infrastructure/Ai/Embedded/Internal/AiService.cs b/src/RookieShop.Infrastructure/Ai/Embedded/Internal/AiService.cs new file mode 100644 index 00000000..57194606 --- /dev/null +++ b/src/RookieShop.Infrastructure/Ai/Embedded/Internal/AiService.cs @@ -0,0 +1,41 @@ +using Microsoft.Extensions.Logging; +using Microsoft.SemanticKernel.Embeddings; +using Pgvector; +using System.Diagnostics; + +namespace RookieShop.Infrastructure.Ai.Embedded.Internal; + +public sealed class AiService(ITextEmbeddingGenerationService embeddingGenerator, ILogger logger) + : IAiService +{ + private const int EmbeddingDimensions = 384; + + public async ValueTask GetEmbeddingAsync(string text, CancellationToken cancellationToken = default) + { + var timestamp = Stopwatch.GetTimestamp(); + + var embedding = await embeddingGenerator.GenerateEmbeddingAsync(text, cancellationToken: cancellationToken); + + embedding = embedding[..EmbeddingDimensions]; + + logger.LogTrace("Generated embedding in {ElapsedMilliseconds}s: '{Text}'", + Stopwatch.GetElapsedTime(timestamp).TotalSeconds, text); + + return new(embedding); + } + + public async ValueTask> GetEmbeddingsAsync(List text, + CancellationToken cancellationToken = default) + { + var timestamp = Stopwatch.GetTimestamp(); + + var embeddings = await embeddingGenerator.GenerateEmbeddingsAsync(text, cancellationToken: cancellationToken); + + var results = embeddings.Select(m => new Vector(m[..EmbeddingDimensions])).ToList(); + + logger.LogTrace("Generated {EmbeddingsCount} embeddings in {ElapsedMilliseconds}s", results.Count, + Stopwatch.GetElapsedTime(timestamp).TotalSeconds); + + return results; + } +} \ No newline at end of file diff --git a/src/RookieShop.Infrastructure/Ai/Extension.cs b/src/RookieShop.Infrastructure/Ai/Extension.cs new file mode 100644 index 00000000..7e279ad7 --- /dev/null +++ b/src/RookieShop.Infrastructure/Ai/Extension.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.SemanticKernel; +using RookieShop.Infrastructure.Ai.Embedded; +using RookieShop.Infrastructure.Ai.Embedded.Internal; + +namespace RookieShop.Infrastructure.Ai; + +public static class Extension +{ + public static IHostApplicationBuilder AddAi(this IHostApplicationBuilder builder) + { + builder.AddAzureOpenAIClient("openai"); + builder.Services.AddOpenAITextEmbeddingGeneration( + builder.Configuration["AIOptions:OpenAI:EmbeddingName"] ?? "text-embedding-3-small"); + + builder.Services.AddSingleton(); + + return builder; + } +} \ No newline at end of file diff --git a/src/RookieShop.Infrastructure/Extension.cs b/src/RookieShop.Infrastructure/Extension.cs index 0b60edfe..aeb23ee9 100644 --- a/src/RookieShop.Infrastructure/Extension.cs +++ b/src/RookieShop.Infrastructure/Extension.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Hosting; +using RookieShop.Infrastructure.Ai; using RookieShop.Infrastructure.Bus; using RookieShop.Infrastructure.Cache; using RookieShop.Infrastructure.Email; @@ -29,7 +30,8 @@ public static IHostApplicationBuilder AddInfrastructure(this IHostApplicationBui .AddStorage() .AddEmail() .AddHealthCheck() - .AddEventBus(); + .AddEventBus() + .AddAi(); return builder; } diff --git a/src/RookieShop.Infrastructure/RookieShop.Infrastructure.csproj b/src/RookieShop.Infrastructure/RookieShop.Infrastructure.csproj index ef9bcc57..874950c9 100644 --- a/src/RookieShop.Infrastructure/RookieShop.Infrastructure.csproj +++ b/src/RookieShop.Infrastructure/RookieShop.Infrastructure.csproj @@ -1,5 +1,9 @@  + + $(NoWarn);SKEXP0001;SKEXP0010;SKEXP0070 + + @@ -26,6 +30,8 @@ + + diff --git a/src/RookieShop.Infrastructure/Swagger/Filters/StronglyTypedIdFilter.cs b/src/RookieShop.Infrastructure/Swagger/Filters/StronglyTypedIdFilter.cs index b195b6f2..7ac8936c 100644 --- a/src/RookieShop.Infrastructure/Swagger/Filters/StronglyTypedIdFilter.cs +++ b/src/RookieShop.Infrastructure/Swagger/Filters/StronglyTypedIdFilter.cs @@ -29,6 +29,5 @@ public void Apply(OpenApiSchema schema, SchemaFilterContext context) schema.Type = "string"; schema.Format = "uuid"; - schema.Example = new OpenApiString(Guid.NewGuid().ToString()); } } \ No newline at end of file diff --git a/src/RookieShop.Persistence/ApplicationDbContext.cs b/src/RookieShop.Persistence/ApplicationDbContext.cs index 0ea1a0a5..0e67b036 100644 --- a/src/RookieShop.Persistence/ApplicationDbContext.cs +++ b/src/RookieShop.Persistence/ApplicationDbContext.cs @@ -40,6 +40,7 @@ public IEnumerable GetDomainEvents() protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); + modelBuilder.HasPostgresExtension(VectorType.Extension); modelBuilder.HasPostgresExtension(UniqueType.Extension); modelBuilder.ApplyConfigurationsFromAssembly(AssemblyReference.DbContextAssembly); } diff --git a/src/RookieShop.Persistence/Configurations/ProductConfiguration.cs b/src/RookieShop.Persistence/Configurations/ProductConfiguration.cs index 1ada84e0..aaaf09ec 100644 --- a/src/RookieShop.Persistence/Configurations/ProductConfiguration.cs +++ b/src/RookieShop.Persistence/Configurations/ProductConfiguration.cs @@ -1,4 +1,4 @@ -using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; using RookieShop.Domain.Constants; using RookieShop.Domain.Entities.ProductAggregator; @@ -48,6 +48,9 @@ public override void Configure(EntityTypeBuilder builder) builder.Property(p => p.TotalReviews) .HasDefaultValue(0); + builder.Property(p => p.Embedding) + .HasColumnType(VectorType.Type); + builder.HasOne(p => p.Category) .WithMany(p => p.Products) .HasForeignKey(p => p.CategoryId) diff --git a/src/RookieShop.Persistence/Constants/VectorType.cs b/src/RookieShop.Persistence/Constants/VectorType.cs new file mode 100644 index 00000000..71de8cb2 --- /dev/null +++ b/src/RookieShop.Persistence/Constants/VectorType.cs @@ -0,0 +1,7 @@ +namespace RookieShop.Persistence.Constants; + +public class VectorType +{ + public const string Extension = "vector"; + public const string Type = "vector(384)"; +} \ No newline at end of file diff --git a/src/RookieShop.Persistence/Extension.cs b/src/RookieShop.Persistence/Extension.cs index a2a02570..366789b5 100644 --- a/src/RookieShop.Persistence/Extension.cs +++ b/src/RookieShop.Persistence/Extension.cs @@ -19,6 +19,7 @@ public static IHostApplicationBuilder AddPersistence(this IHostApplicationBuilde { dbContextOptionsBuilder.UseNpgsql(optionsBuilder => { + optionsBuilder.UseVector(); optionsBuilder.MigrationsAssembly(AssemblyReference.DbContextAssembly.FullName); optionsBuilder.EnableRetryOnFailure(15, TimeSpan.FromSeconds(30), null); }) diff --git a/src/RookieShop.Persistence/Migrations/20240526072943_Initalize-database.Designer.cs b/src/RookieShop.Persistence/Migrations/20240608160016_Initialize-database.Designer.cs similarity index 82% rename from src/RookieShop.Persistence/Migrations/20240526072943_Initalize-database.Designer.cs rename to src/RookieShop.Persistence/Migrations/20240608160016_Initialize-database.Designer.cs index 78451dd7..6c0d4289 100644 --- a/src/RookieShop.Persistence/Migrations/20240526072943_Initalize-database.Designer.cs +++ b/src/RookieShop.Persistence/Migrations/20240608160016_Initialize-database.Designer.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Pgvector; using RookieShop.Persistence; #nullable disable @@ -12,18 +13,19 @@ namespace RookieShop.Persistence.Migrations { [DbContext(typeof(ApplicationDbContext))] - [Migration("20240526072943_Initalize-database")] - partial class Initalizedatabase + [Migration("20240608160016_Initialize-database")] + partial class Initializedatabase { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.5") + .HasAnnotation("ProductVersion", "8.0.6") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "uuid-ossp"); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "vector"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("RookieShop.Domain.Entities.CategoryAggregator.Category", b => @@ -37,7 +39,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 788, DateTimeKind.Utc).AddTicks(5283)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 704, DateTimeKind.Utc).AddTicks(4226)) .HasColumnName("created_date"); b.Property("Description") @@ -46,6 +48,10 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("character varying(1000)") .HasColumnName("description"); + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + b.Property("Name") .IsRequired() .HasMaxLength(50) @@ -55,14 +61,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 788, DateTimeKind.Utc).AddTicks(5538)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 704, DateTimeKind.Utc).AddTicks(4626)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("342540df-e719-4f7e-8142-896fd3c057e0")) + .HasDefaultValue(new Guid("bf570dc2-41e6-4f6a-9383-c1d084c32b84")) .HasColumnName("version"); b.HasKey("Id") @@ -73,51 +79,57 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("447e4b38-8996-4763-aa81-f43eb8b5d440"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(766), + Id = new Guid("8d7d55f7-cf10-42d3-936f-7cb6150130d3"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(895), Description = "A book is a medium for recording information in the form of writing or images, typically composed of many pages bound together and protected by a cover.", + IsDeleted = false, Name = "Books", - Version = new Guid("4102abec-302c-4f9a-bdce-29fca05adae5") + Version = new Guid("1e2c7f60-7dcc-421a-816e-3681209c2fa1") }, new { - Id = new Guid("361c599c-138b-4cce-92a6-a85cc39678c8"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(773), + Id = new Guid("4736ffeb-cfce-4f68-b527-b9f6d5444f7e"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(913), Description = "A toy is an item that is used in play, especially one designed for such use. Playing with toys can be an enjoyable means of training young children for life in society.", + IsDeleted = false, Name = "Toys", - Version = new Guid("b3c83e68-38ba-4fde-84eb-e9987de0c5b4") + Version = new Guid("9002d6d4-678a-458e-9aae-ecb11da618f6") }, new { - Id = new Guid("47493803-5737-4621-b29e-ae5a8b0e1ce0"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(776), + Id = new Guid("9cf92ba3-2382-4ef4-9899-5d497cce0efe"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(927), Description = "A comic book, also called comic magazine or (in the United Kingdom and Ireland) simply comic, is a publication that consists of comics art in the form of sequential juxtaposed panels that represent individual scenes.", + IsDeleted = false, Name = "Comics", - Version = new Guid("fbaf7758-5bb0-441f-9d39-5672dc853950") + Version = new Guid("e7cfc774-d83a-4ef2-876b-0f9d786a6951") }, new { - Id = new Guid("b8e91b58-7158-45fa-88b5-96fdac4dcbb9"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(787), + Id = new Guid("624dcddd-9b23-47e7-8e58-54fb92ffd489"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(929), Description = "Artwork is a term that describes art that is created to be appreciated for its own sake. It generally refers to visual art, such as paintings, sculptures, and printmaking.", + IsDeleted = false, Name = "Artworks", - Version = new Guid("f389691a-1562-4ad4-8287-f2ea9fd1a257") + Version = new Guid("d27c9645-c53c-4ee7-b694-8031f1004886") }, new { - Id = new Guid("f7636d3b-7f3e-4040-9beb-73f860041310"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(789), + Id = new Guid("52bfc10c-7f88-4105-a002-55197ff308ce"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(932), Description = "A souvenir is an object that is kept as a reminder of a person, place, or event. The object itself may have intrinsic value, or be a symbol of experience.", + IsDeleted = false, Name = "Souvenirs", - Version = new Guid("e20e02eb-a112-421c-b37e-5545c4d6aaf9") + Version = new Guid("bfb9afe1-9c8a-4c1c-b19b-482755ccc16d") }, new { - Id = new Guid("05416004-bdec-48d2-8e7f-767873a5a228"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(791), + Id = new Guid("8eebe4a6-5e66-4655-91a6-b35d50edf167"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(935), Description = "Stationery is a mass noun referring to commercially manufactured writing materials, including cut paper, envelopes, writing implements, continuous form paper, and other office supplies.", + IsDeleted = false, Name = "Stationery", - Version = new Guid("93af261f-b55e-49a1-903a-33a1ffda21ec") + Version = new Guid("344ca78f-a129-4455-937c-a9254b2ecd85") }); }); @@ -136,7 +148,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(2069)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(2354)) .HasColumnName("created_date"); b.Property("Email") @@ -168,14 +180,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(2305)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(2590)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("bee3dc2d-b9ef-4a18-b54e-17f6d7c52ebc")) + .HasDefaultValue(new Guid("0c851886-6ca3-4901-afb1-3a9c232eb1af")) .HasColumnName("version"); b.HasKey("Id") @@ -186,26 +198,26 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("eebda025-0380-4581-92af-57590a790160"), + Id = new Guid("3906a8e8-f63c-4172-991c-3ec2c86a1dc3"), AccountId = new Guid("7055bbfe-25c6-4b33-98cd-fc2b9fb4bb1a"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(8972), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(1003), Email = "nguyenxuannhan407@gmail.com", Gender = (byte)1, IsDeleted = false, Name = "Nhan Nguyen", Phone = "0123456789", - Version = new Guid("0f3dcc40-38e5-4bf4-9ab7-96505fe666ba") + Version = new Guid("4c4dbc31-e498-4c38-9fa1-97318e29e737") }, new { - Id = new Guid("12da5fa6-d57c-4272-b463-06d08c502138"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(8988), + Id = new Guid("63ccb9b4-daeb-4e12-a6ed-7a8421cbf6a5"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(1025), Email = "nguyenxuannhan.dev@gmail.com", Gender = (byte)1, IsDeleted = false, Name = "Fox Min Chan", Phone = "0123456789", - Version = new Guid("863d6d38-b255-4650-9017-135f59fb0f32") + Version = new Guid("61618209-9dd9-442b-815f-669419835118") }); }); @@ -226,7 +238,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 790, DateTimeKind.Utc).AddTicks(154)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(2389)) .HasColumnName("created_date"); b.Property("CustomerId") @@ -244,14 +256,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 790, DateTimeKind.Utc).AddTicks(368)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(2698)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("af8e97c9-85b9-4d6c-9659-2c01e31894ee")) + .HasDefaultValue(new Guid("34b29e8f-3924-4449-9989-9a197a0f8370")) .HasColumnName("version"); b.HasKey("Id") @@ -277,7 +289,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 791, DateTimeKind.Utc).AddTicks(3000)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 708, DateTimeKind.Utc).AddTicks(949)) .HasColumnName("created_date"); b.Property("CustomerId") @@ -295,14 +307,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 791, DateTimeKind.Utc).AddTicks(3258)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 708, DateTimeKind.Utc).AddTicks(1311)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("eb765674-9cb9-4a7b-9795-615f6e6192bc")) + .HasDefaultValue(new Guid("96d2ce25-2ba4-478b-bd4d-59e5bed0132d")) .HasColumnName("version"); b.HasKey("Id") @@ -327,7 +339,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 794, DateTimeKind.Utc).AddTicks(1361)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 711, DateTimeKind.Utc).AddTicks(5326)) .HasColumnName("created_date"); b.Property("Price") @@ -341,14 +353,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 794, DateTimeKind.Utc).AddTicks(2058)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 711, DateTimeKind.Utc).AddTicks(5612)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("b1b1ca74-c1a5-4460-8921-616c712b686c")) + .HasDefaultValue(new Guid("7f826faf-581f-456c-b9b7-d561f96f088c")) .HasColumnName("version"); b.HasKey("OrderId", "ProductId") @@ -381,7 +393,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 795, DateTimeKind.Utc).AddTicks(1748)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 712, DateTimeKind.Utc).AddTicks(5951)) .HasColumnName("created_date"); b.Property("Description") @@ -390,6 +402,11 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("character varying(1000)") .HasColumnName("description"); + b.Property("Embedding") + .IsRequired() + .HasColumnType("vector(384)") + .HasColumnName("embedding"); + b.Property("ImageName") .HasMaxLength(50) .HasColumnType("character varying(50)") @@ -424,14 +441,14 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 795, DateTimeKind.Utc).AddTicks(1990)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 712, DateTimeKind.Utc).AddTicks(6265)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("48ae7fba-a60c-4f09-95ec-1477f6f65318")) + .HasDefaultValue(new Guid("1fd494dd-341f-46ca-a3fb-b4c8f62acd79")) .HasColumnName("version"); b.HasKey("Id") diff --git a/src/RookieShop.Persistence/Migrations/20240526072943_Initalize-database.cs b/src/RookieShop.Persistence/Migrations/20240608160016_Initialize-database.cs similarity index 72% rename from src/RookieShop.Persistence/Migrations/20240526072943_Initalize-database.cs rename to src/RookieShop.Persistence/Migrations/20240608160016_Initialize-database.cs index a2d1ceff..02a8fb43 100644 --- a/src/RookieShop.Persistence/Migrations/20240526072943_Initalize-database.cs +++ b/src/RookieShop.Persistence/Migrations/20240608160016_Initialize-database.cs @@ -1,5 +1,6 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; +using Pgvector; #nullable disable @@ -8,13 +9,14 @@ namespace RookieShop.Persistence.Migrations { /// - public partial class Initalizedatabase : Migration + public partial class Initializedatabase : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.AlterDatabase() - .Annotation("Npgsql:PostgresExtension:uuid-ossp", ",,"); + .Annotation("Npgsql:PostgresExtension:uuid-ossp", ",,") + .Annotation("Npgsql:PostgresExtension:vector", ",,"); migrationBuilder.CreateTable( name: "categories", @@ -23,9 +25,10 @@ protected override void Up(MigrationBuilder migrationBuilder) id = table.Column(type: "uuid", nullable: false, defaultValueSql: "uuid_generate_v4()"), name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), description = table.Column(type: "character varying(1000)", maxLength: 1000, nullable: false), - created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 788, DateTimeKind.Utc).AddTicks(5283)), - update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 788, DateTimeKind.Utc).AddTicks(5538)), - version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("342540df-e719-4f7e-8142-896fd3c057e0")) + is_deleted = table.Column(type: "boolean", nullable: false), + created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 704, DateTimeKind.Utc).AddTicks(4226)), + update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 704, DateTimeKind.Utc).AddTicks(4626)), + version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("bf570dc2-41e6-4f6a-9383-c1d084c32b84")) }, constraints: table => { @@ -43,9 +46,9 @@ protected override void Up(MigrationBuilder migrationBuilder) gender = table.Column(type: "smallint", nullable: false), account_id = table.Column(type: "uuid", nullable: true), is_deleted = table.Column(type: "boolean", nullable: false), - created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(2069)), - update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(2305)), - version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("bee3dc2d-b9ef-4a18-b54e-17f6d7c52ebc")) + created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(2354)), + update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(2590)), + version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("0c851886-6ca3-4901-afb1-3a9c232eb1af")) }, constraints: table => { @@ -65,10 +68,11 @@ protected override void Up(MigrationBuilder migrationBuilder) average_rating = table.Column(type: "double precision", nullable: false, defaultValue: 0.0), total_reviews = table.Column(type: "integer", nullable: false, defaultValue: 0), category_id = table.Column(type: "uuid", nullable: true), + embedding = table.Column(type: "vector(384)", nullable: false), is_deleted = table.Column(type: "boolean", nullable: false), - created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 795, DateTimeKind.Utc).AddTicks(1748)), - update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 795, DateTimeKind.Utc).AddTicks(1990)), - version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("48ae7fba-a60c-4f09-95ec-1477f6f65318")), + created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 712, DateTimeKind.Utc).AddTicks(5951)), + update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 712, DateTimeKind.Utc).AddTicks(6265)), + version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("1fd494dd-341f-46ca-a3fb-b4c8f62acd79")), price = table.Column(type: "jsonb", nullable: false) }, constraints: table => @@ -96,9 +100,9 @@ protected override void Up(MigrationBuilder migrationBuilder) order_status = table.Column(type: "smallint", nullable: false), payment_method = table.Column(type: "smallint", nullable: false), customer_id = table.Column(type: "uuid", nullable: false), - created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 791, DateTimeKind.Utc).AddTicks(3000)), - update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 791, DateTimeKind.Utc).AddTicks(3258)), - version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("eb765674-9cb9-4a7b-9795-615f6e6192bc")) + created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 708, DateTimeKind.Utc).AddTicks(949)), + update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 708, DateTimeKind.Utc).AddTicks(1311)), + version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("96d2ce25-2ba4-478b-bd4d-59e5bed0132d")) }, constraints: table => { @@ -120,9 +124,9 @@ protected override void Up(MigrationBuilder migrationBuilder) rating = table.Column(type: "integer", nullable: false), customer_id = table.Column(type: "uuid", nullable: true), product_id = table.Column(type: "uuid", nullable: false), - created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 790, DateTimeKind.Utc).AddTicks(154)), - update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 790, DateTimeKind.Utc).AddTicks(368)), - version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("af8e97c9-85b9-4d6c-9659-2c01e31894ee")) + created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(2389)), + update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(2698)), + version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("34b29e8f-3924-4449-9989-9a197a0f8370")) }, constraints: table => { @@ -149,9 +153,9 @@ protected override void Up(MigrationBuilder migrationBuilder) order_id = table.Column(type: "uuid", nullable: false), price = table.Column(type: "numeric", nullable: false), quantity = table.Column(type: "integer", nullable: false), - created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 794, DateTimeKind.Utc).AddTicks(1361)), - update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 5, 26, 7, 29, 42, 794, DateTimeKind.Utc).AddTicks(2058)), - version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("b1b1ca74-c1a5-4460-8921-616c712b686c")) + created_date = table.Column(type: "timestamp with time zone", nullable: false, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 711, DateTimeKind.Utc).AddTicks(5326)), + update_date = table.Column(type: "timestamp with time zone", nullable: true, defaultValue: new DateTime(2024, 6, 8, 16, 0, 16, 711, DateTimeKind.Utc).AddTicks(5612)), + version = table.Column(type: "uuid", nullable: false, defaultValue: new Guid("7f826faf-581f-456c-b9b7-d561f96f088c")) }, constraints: table => { @@ -172,15 +176,15 @@ protected override void Up(MigrationBuilder migrationBuilder) migrationBuilder.InsertData( table: "categories", - columns: new[] { "id", "created_date", "description", "name", "version" }, + columns: new[] { "id", "created_date", "description", "is_deleted", "name", "version" }, values: new object[,] { - { new Guid("05416004-bdec-48d2-8e7f-767873a5a228"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(791), "Stationery is a mass noun referring to commercially manufactured writing materials, including cut paper, envelopes, writing implements, continuous form paper, and other office supplies.", "Stationery", new Guid("93af261f-b55e-49a1-903a-33a1ffda21ec") }, - { new Guid("361c599c-138b-4cce-92a6-a85cc39678c8"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(773), "A toy is an item that is used in play, especially one designed for such use. Playing with toys can be an enjoyable means of training young children for life in society.", "Toys", new Guid("b3c83e68-38ba-4fde-84eb-e9987de0c5b4") }, - { new Guid("447e4b38-8996-4763-aa81-f43eb8b5d440"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(766), "A book is a medium for recording information in the form of writing or images, typically composed of many pages bound together and protected by a cover.", "Books", new Guid("4102abec-302c-4f9a-bdce-29fca05adae5") }, - { new Guid("47493803-5737-4621-b29e-ae5a8b0e1ce0"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(776), "A comic book, also called comic magazine or (in the United Kingdom and Ireland) simply comic, is a publication that consists of comics art in the form of sequential juxtaposed panels that represent individual scenes.", "Comics", new Guid("fbaf7758-5bb0-441f-9d39-5672dc853950") }, - { new Guid("b8e91b58-7158-45fa-88b5-96fdac4dcbb9"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(787), "Artwork is a term that describes art that is created to be appreciated for its own sake. It generally refers to visual art, such as paintings, sculptures, and printmaking.", "Artworks", new Guid("f389691a-1562-4ad4-8287-f2ea9fd1a257") }, - { new Guid("f7636d3b-7f3e-4040-9beb-73f860041310"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(789), "A souvenir is an object that is kept as a reminder of a person, place, or event. The object itself may have intrinsic value, or be a symbol of experience.", "Souvenirs", new Guid("e20e02eb-a112-421c-b37e-5545c4d6aaf9") } + { new Guid("4736ffeb-cfce-4f68-b527-b9f6d5444f7e"), new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(913), "A toy is an item that is used in play, especially one designed for such use. Playing with toys can be an enjoyable means of training young children for life in society.", false, "Toys", new Guid("9002d6d4-678a-458e-9aae-ecb11da618f6") }, + { new Guid("52bfc10c-7f88-4105-a002-55197ff308ce"), new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(932), "A souvenir is an object that is kept as a reminder of a person, place, or event. The object itself may have intrinsic value, or be a symbol of experience.", false, "Souvenirs", new Guid("bfb9afe1-9c8a-4c1c-b19b-482755ccc16d") }, + { new Guid("624dcddd-9b23-47e7-8e58-54fb92ffd489"), new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(929), "Artwork is a term that describes art that is created to be appreciated for its own sake. It generally refers to visual art, such as paintings, sculptures, and printmaking.", false, "Artworks", new Guid("d27c9645-c53c-4ee7-b694-8031f1004886") }, + { new Guid("8d7d55f7-cf10-42d3-936f-7cb6150130d3"), new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(895), "A book is a medium for recording information in the form of writing or images, typically composed of many pages bound together and protected by a cover.", false, "Books", new Guid("1e2c7f60-7dcc-421a-816e-3681209c2fa1") }, + { new Guid("8eebe4a6-5e66-4655-91a6-b35d50edf167"), new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(935), "Stationery is a mass noun referring to commercially manufactured writing materials, including cut paper, envelopes, writing implements, continuous form paper, and other office supplies.", false, "Stationery", new Guid("344ca78f-a129-4455-937c-a9254b2ecd85") }, + { new Guid("9cf92ba3-2382-4ef4-9899-5d497cce0efe"), new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(927), "A comic book, also called comic magazine or (in the United Kingdom and Ireland) simply comic, is a publication that consists of comics art in the form of sequential juxtaposed panels that represent individual scenes.", false, "Comics", new Guid("e7cfc774-d83a-4ef2-876b-0f9d786a6951") } }); migrationBuilder.InsertData( @@ -188,8 +192,8 @@ protected override void Up(MigrationBuilder migrationBuilder) columns: new[] { "id", "account_id", "created_date", "email", "gender", "is_deleted", "name", "phone", "version" }, values: new object[,] { - { new Guid("12da5fa6-d57c-4272-b463-06d08c502138"), null, new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(8988), "nguyenxuannhan.dev@gmail.com", (byte)1, false, "Fox Min Chan", "0123456789", new Guid("863d6d38-b255-4650-9017-135f59fb0f32") }, - { new Guid("eebda025-0380-4581-92af-57590a790160"), new Guid("7055bbfe-25c6-4b33-98cd-fc2b9fb4bb1a"), new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(8972), "nguyenxuannhan407@gmail.com", (byte)1, false, "Nhan Nguyen", "0123456789", new Guid("0f3dcc40-38e5-4bf4-9ab7-96505fe666ba") } + { new Guid("3906a8e8-f63c-4172-991c-3ec2c86a1dc3"), new Guid("7055bbfe-25c6-4b33-98cd-fc2b9fb4bb1a"), new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(1003), "nguyenxuannhan407@gmail.com", (byte)1, false, "Nhan Nguyen", "0123456789", new Guid("4c4dbc31-e498-4c38-9fa1-97318e29e737") }, + { new Guid("63ccb9b4-daeb-4e12-a6ed-7a8421cbf6a5"), null, new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(1025), "nguyenxuannhan.dev@gmail.com", (byte)1, false, "Fox Min Chan", "0123456789", new Guid("61618209-9dd9-442b-815f-669419835118") } }); migrationBuilder.CreateIndex( diff --git a/src/RookieShop.Persistence/Migrations/ApplicationDbContextModelSnapshot.cs b/src/RookieShop.Persistence/Migrations/ApplicationDbContextModelSnapshot.cs index b823927f..cb0dd966 100644 --- a/src/RookieShop.Persistence/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/RookieShop.Persistence/Migrations/ApplicationDbContextModelSnapshot.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Pgvector; using RookieShop.Persistence; #nullable disable @@ -17,10 +18,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "8.0.5") + .HasAnnotation("ProductVersion", "8.0.6") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "uuid-ossp"); + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "vector"); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("RookieShop.Domain.Entities.CategoryAggregator.Category", b => @@ -34,7 +36,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 788, DateTimeKind.Utc).AddTicks(5283)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 704, DateTimeKind.Utc).AddTicks(4226)) .HasColumnName("created_date"); b.Property("Description") @@ -43,6 +45,10 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("character varying(1000)") .HasColumnName("description"); + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + b.Property("Name") .IsRequired() .HasMaxLength(50) @@ -52,14 +58,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 788, DateTimeKind.Utc).AddTicks(5538)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 704, DateTimeKind.Utc).AddTicks(4626)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("342540df-e719-4f7e-8142-896fd3c057e0")) + .HasDefaultValue(new Guid("bf570dc2-41e6-4f6a-9383-c1d084c32b84")) .HasColumnName("version"); b.HasKey("Id") @@ -70,51 +76,57 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("447e4b38-8996-4763-aa81-f43eb8b5d440"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(766), + Id = new Guid("8d7d55f7-cf10-42d3-936f-7cb6150130d3"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(895), Description = "A book is a medium for recording information in the form of writing or images, typically composed of many pages bound together and protected by a cover.", + IsDeleted = false, Name = "Books", - Version = new Guid("4102abec-302c-4f9a-bdce-29fca05adae5") + Version = new Guid("1e2c7f60-7dcc-421a-816e-3681209c2fa1") }, new { - Id = new Guid("361c599c-138b-4cce-92a6-a85cc39678c8"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(773), + Id = new Guid("4736ffeb-cfce-4f68-b527-b9f6d5444f7e"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(913), Description = "A toy is an item that is used in play, especially one designed for such use. Playing with toys can be an enjoyable means of training young children for life in society.", + IsDeleted = false, Name = "Toys", - Version = new Guid("b3c83e68-38ba-4fde-84eb-e9987de0c5b4") + Version = new Guid("9002d6d4-678a-458e-9aae-ecb11da618f6") }, new { - Id = new Guid("47493803-5737-4621-b29e-ae5a8b0e1ce0"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(776), + Id = new Guid("9cf92ba3-2382-4ef4-9899-5d497cce0efe"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(927), Description = "A comic book, also called comic magazine or (in the United Kingdom and Ireland) simply comic, is a publication that consists of comics art in the form of sequential juxtaposed panels that represent individual scenes.", + IsDeleted = false, Name = "Comics", - Version = new Guid("fbaf7758-5bb0-441f-9d39-5672dc853950") + Version = new Guid("e7cfc774-d83a-4ef2-876b-0f9d786a6951") }, new { - Id = new Guid("b8e91b58-7158-45fa-88b5-96fdac4dcbb9"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(787), + Id = new Guid("624dcddd-9b23-47e7-8e58-54fb92ffd489"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(929), Description = "Artwork is a term that describes art that is created to be appreciated for its own sake. It generally refers to visual art, such as paintings, sculptures, and printmaking.", + IsDeleted = false, Name = "Artworks", - Version = new Guid("f389691a-1562-4ad4-8287-f2ea9fd1a257") + Version = new Guid("d27c9645-c53c-4ee7-b694-8031f1004886") }, new { - Id = new Guid("f7636d3b-7f3e-4040-9beb-73f860041310"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(789), + Id = new Guid("52bfc10c-7f88-4105-a002-55197ff308ce"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(932), Description = "A souvenir is an object that is kept as a reminder of a person, place, or event. The object itself may have intrinsic value, or be a symbol of experience.", + IsDeleted = false, Name = "Souvenirs", - Version = new Guid("e20e02eb-a112-421c-b37e-5545c4d6aaf9") + Version = new Guid("bfb9afe1-9c8a-4c1c-b19b-482755ccc16d") }, new { - Id = new Guid("05416004-bdec-48d2-8e7f-767873a5a228"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(791), + Id = new Guid("8eebe4a6-5e66-4655-91a6-b35d50edf167"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(935), Description = "Stationery is a mass noun referring to commercially manufactured writing materials, including cut paper, envelopes, writing implements, continuous form paper, and other office supplies.", + IsDeleted = false, Name = "Stationery", - Version = new Guid("93af261f-b55e-49a1-903a-33a1ffda21ec") + Version = new Guid("344ca78f-a129-4455-937c-a9254b2ecd85") }); }); @@ -133,7 +145,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(2069)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(2354)) .HasColumnName("created_date"); b.Property("Email") @@ -165,14 +177,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(2305)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 705, DateTimeKind.Utc).AddTicks(2590)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("bee3dc2d-b9ef-4a18-b54e-17f6d7c52ebc")) + .HasDefaultValue(new Guid("0c851886-6ca3-4901-afb1-3a9c232eb1af")) .HasColumnName("version"); b.HasKey("Id") @@ -183,26 +195,26 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasData( new { - Id = new Guid("eebda025-0380-4581-92af-57590a790160"), + Id = new Guid("3906a8e8-f63c-4172-991c-3ec2c86a1dc3"), AccountId = new Guid("7055bbfe-25c6-4b33-98cd-fc2b9fb4bb1a"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(8972), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(1003), Email = "nguyenxuannhan407@gmail.com", Gender = (byte)1, IsDeleted = false, Name = "Nhan Nguyen", Phone = "0123456789", - Version = new Guid("0f3dcc40-38e5-4bf4-9ab7-96505fe666ba") + Version = new Guid("4c4dbc31-e498-4c38-9fa1-97318e29e737") }, new { - Id = new Guid("12da5fa6-d57c-4272-b463-06d08c502138"), - CreatedDate = new DateTime(2024, 5, 26, 7, 29, 42, 789, DateTimeKind.Utc).AddTicks(8988), + Id = new Guid("63ccb9b4-daeb-4e12-a6ed-7a8421cbf6a5"), + CreatedDate = new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(1025), Email = "nguyenxuannhan.dev@gmail.com", Gender = (byte)1, IsDeleted = false, Name = "Fox Min Chan", Phone = "0123456789", - Version = new Guid("863d6d38-b255-4650-9017-135f59fb0f32") + Version = new Guid("61618209-9dd9-442b-815f-669419835118") }); }); @@ -223,7 +235,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 790, DateTimeKind.Utc).AddTicks(154)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(2389)) .HasColumnName("created_date"); b.Property("CustomerId") @@ -241,14 +253,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 790, DateTimeKind.Utc).AddTicks(368)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 706, DateTimeKind.Utc).AddTicks(2698)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("af8e97c9-85b9-4d6c-9659-2c01e31894ee")) + .HasDefaultValue(new Guid("34b29e8f-3924-4449-9989-9a197a0f8370")) .HasColumnName("version"); b.HasKey("Id") @@ -274,7 +286,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 791, DateTimeKind.Utc).AddTicks(3000)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 708, DateTimeKind.Utc).AddTicks(949)) .HasColumnName("created_date"); b.Property("CustomerId") @@ -292,14 +304,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 791, DateTimeKind.Utc).AddTicks(3258)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 708, DateTimeKind.Utc).AddTicks(1311)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("eb765674-9cb9-4a7b-9795-615f6e6192bc")) + .HasDefaultValue(new Guid("96d2ce25-2ba4-478b-bd4d-59e5bed0132d")) .HasColumnName("version"); b.HasKey("Id") @@ -324,7 +336,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 794, DateTimeKind.Utc).AddTicks(1361)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 711, DateTimeKind.Utc).AddTicks(5326)) .HasColumnName("created_date"); b.Property("Price") @@ -338,14 +350,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 794, DateTimeKind.Utc).AddTicks(2058)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 711, DateTimeKind.Utc).AddTicks(5612)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("b1b1ca74-c1a5-4460-8921-616c712b686c")) + .HasDefaultValue(new Guid("7f826faf-581f-456c-b9b7-d561f96f088c")) .HasColumnName("version"); b.HasKey("OrderId", "ProductId") @@ -378,7 +390,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("CreatedDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 795, DateTimeKind.Utc).AddTicks(1748)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 712, DateTimeKind.Utc).AddTicks(5951)) .HasColumnName("created_date"); b.Property("Description") @@ -387,6 +399,11 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("character varying(1000)") .HasColumnName("description"); + b.Property("Embedding") + .IsRequired() + .HasColumnType("vector(384)") + .HasColumnName("embedding"); + b.Property("ImageName") .HasMaxLength(50) .HasColumnType("character varying(50)") @@ -421,14 +438,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.Property("UpdateDate") .ValueGeneratedOnAdd() .HasColumnType("timestamp with time zone") - .HasDefaultValue(new DateTime(2024, 5, 26, 7, 29, 42, 795, DateTimeKind.Utc).AddTicks(1990)) + .HasDefaultValue(new DateTime(2024, 6, 8, 16, 0, 16, 712, DateTimeKind.Utc).AddTicks(6265)) .HasColumnName("update_date"); b.Property("Version") .IsConcurrencyToken() .ValueGeneratedOnAdd() .HasColumnType("uuid") - .HasDefaultValue(new Guid("48ae7fba-a60c-4f09-95ec-1477f6f65318")) + .HasDefaultValue(new Guid("1fd494dd-341f-46ca-a3fb-b4c8f62acd79")) .HasColumnName("version"); b.HasKey("Id") diff --git a/src/RookieShop.Persistence/RookieShop.Persistence.csproj b/src/RookieShop.Persistence/RookieShop.Persistence.csproj index bb36959b..46b7fbeb 100644 --- a/src/RookieShop.Persistence/RookieShop.Persistence.csproj +++ b/src/RookieShop.Persistence/RookieShop.Persistence.csproj @@ -12,9 +12,6 @@ - - - diff --git a/tests/RookieShop.UnitTests/Application/ProductHandlerTest/CreateProduct.cs b/tests/RookieShop.UnitTests/Application/ProductHandlerTest/CreateProduct.cs index 31e5d007..ac68e475 100644 --- a/tests/RookieShop.UnitTests/Application/ProductHandlerTest/CreateProduct.cs +++ b/tests/RookieShop.UnitTests/Application/ProductHandlerTest/CreateProduct.cs @@ -2,6 +2,7 @@ using RookieShop.Application.Products.Commands.Create; using RookieShop.Domain.Entities.ProductAggregator; using RookieShop.Domain.SharedKernel; +using RookieShop.Infrastructure.Ai.Embedded; using RookieShop.Infrastructure.Storage.Azurite; using RookieShop.UnitTests.Builders; @@ -14,10 +15,11 @@ public sealed class CreateProduct public CreateProduct() { + Mock aiServiceMock = new(); Mock> loggerMock = new(); Mock azuriteServiceMock = new(); _repositoryMock = new(); - _handler = new(_repositoryMock.Object, azuriteServiceMock.Object, loggerMock.Object); + _handler = new(_repositoryMock.Object, azuriteServiceMock.Object, aiServiceMock.Object, loggerMock.Object); } private static Product CreateProductEntity() => diff --git a/ui/storefront/Program.cs b/ui/storefront/Program.cs index 43b10c3a..0901e5af 100644 --- a/ui/storefront/Program.cs +++ b/ui/storefront/Program.cs @@ -62,11 +62,12 @@ if (!app.Environment.IsDevelopment()) { - app.UseHttpsRedirection(); app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } +app.UseHttpsRedirection(); + app.UseMiddleware(); app.UseMiddleware();