diff --git a/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageFtSearchConfiguration.cs b/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageFtSearchConfiguration.cs new file mode 100644 index 0000000..de2977a --- /dev/null +++ b/ImageBase.WebApp/Data/ConfigurationDataBase/ConfigurationPostgreSQL/ImageFtSearchConfiguration.cs @@ -0,0 +1,27 @@ +using ImageBase.WebApp.Data.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + + +namespace ImageBase.WebApp.Data.ConfigurationDataBase.ConfigurationPostgreSQL +{ + public class ImageFtSearchConfiguration + { + public ImageFtSearchConfiguration(EntityTypeBuilder entityBuilder) + { + entityBuilder.HasKey(ifts => ifts.Id); + entityBuilder.Property(ifts => ifts.Id).HasColumnName("id"); + entityBuilder.HasOne(ifts => ifts.Image).WithOne(i => i.ImageFtSearch).HasForeignKey(ifts => ifts.ImageId).OnDelete(DeleteBehavior.Cascade); + + entityBuilder.Property(ifts => ifts.ImageId).HasColumnName("image_id").IsRequired(); + entityBuilder.Property(ifts => ifts.ImageVector).HasColumnName("image_vector").IsRequired(); + entityBuilder.Property(ifts => ifts.Id).ValueGeneratedOnAdd(); + + entityBuilder.ToTable("images_ft_search"); + } + } +} diff --git a/ImageBase.WebApp/Data/Models/Image.cs b/ImageBase.WebApp/Data/Models/Image.cs index 556e705..1540619 100644 --- a/ImageBase.WebApp/Data/Models/Image.cs +++ b/ImageBase.WebApp/Data/Models/Image.cs @@ -10,7 +10,7 @@ public class Image: BaseEntity public string Title { get; set; } public string Description { get; set; } public string KeyWords { get; set; } - + public ImageFtSearch ImageFtSearch { get; set; } public List ImageCatalogs { get; set; } } } diff --git a/ImageBase.WebApp/Data/Models/ImageFtSearch.cs b/ImageBase.WebApp/Data/Models/ImageFtSearch.cs new file mode 100644 index 0000000..abd2955 --- /dev/null +++ b/ImageBase.WebApp/Data/Models/ImageFtSearch.cs @@ -0,0 +1,15 @@ +using NpgsqlTypes; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace ImageBase.WebApp.Data.Models +{ + public class ImageFtSearch:BaseEntity + { + public long ImageId { get; set; } + public NpgsqlTsVector ImageVector { get; set; } + public Image Image { get; set; } + } +} diff --git a/ImageBase.WebApp/ImageBase.WebApp.csproj b/ImageBase.WebApp/ImageBase.WebApp.csproj index c5f5d37..26c588e 100644 --- a/ImageBase.WebApp/ImageBase.WebApp.csproj +++ b/ImageBase.WebApp/ImageBase.WebApp.csproj @@ -5,6 +5,11 @@ 4b34e02f-e6d2-4333-96e0-8ea1851999bb + + + + + diff --git a/ImageBase.WebApp/Migrations/20201204105242_FTSIntegration.Designer.cs b/ImageBase.WebApp/Migrations/20201204105242_FTSIntegration.Designer.cs new file mode 100644 index 0000000..864d912 --- /dev/null +++ b/ImageBase.WebApp/Migrations/20201204105242_FTSIntegration.Designer.cs @@ -0,0 +1,444 @@ +// +using System; +using ImageBase.WebApp.Repositories; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +namespace ImageBase.WebApp.Migrations +{ + [DbContext(typeof(AspPostgreSQLContext))] + [Migration("20201204105242_FTSIntegration")] + partial class FTSIntegration + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Authentication.User", b => + { + b.Property("Id") + .HasColumnName("id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnName("access_failed_count") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("concurrency_stamp") + .HasColumnType("text"); + + b.Property("Email") + .HasColumnName("email") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("EmailConfirmed") + .HasColumnName("email_confirmed") + .HasColumnType("boolean"); + + b.Property("LockoutEnabled") + .HasColumnName("lockout_enabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnName("lockout_end") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasColumnName("normalized_email") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("NormalizedUserName") + .HasColumnName("normalized_user_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("PasswordHash") + .HasColumnName("password_hash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnName("phone_number") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnName("phone_number_confirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnName("security_stamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnName("two_factor_enabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasColumnName("user_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasName("UserNameIndex"); + + b.ToTable("users"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Catalog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Name") + .IsRequired() + .HasColumnName("name") + .HasColumnType("character varying(30)") + .HasMaxLength(30); + + b.Property("ParentCatalogId") + .HasColumnName("parent_catalog_id") + .HasColumnType("integer"); + + b.Property("UserId") + .HasColumnName("user_id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("ParentCatalogId"); + + b.HasIndex("UserId"); + + b.ToTable("catalogs"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Image", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("Description") + .HasColumnName("description") + .HasColumnType("text"); + + b.Property("KeyWords") + .HasColumnName("key_words") + .HasColumnType("text"); + + b.Property("Title") + .IsRequired() + .HasColumnName("title") + .HasColumnType("character varying(200)") + .HasMaxLength(200); + + b.HasKey("Id"); + + b.ToTable("images"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageCatalog", b => + { + b.Property("CatalogId") + .HasColumnName("catalog_id") + .HasColumnType("integer"); + + b.Property("ImageId") + .HasColumnName("image_id") + .HasColumnType("bigint"); + + b.HasKey("CatalogId", "ImageId"); + + b.HasIndex("ImageId"); + + b.ToTable("images_catalogs"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageFtSearch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ImageId") + .HasColumnName("image_id") + .HasColumnType("bigint"); + + b.Property("ImageVector") + .IsRequired() + .HasColumnName("image_vector") + .HasColumnType("tsvector"); + + b.HasKey("Id"); + + b.HasIndex("ImageId") + .IsUnique(); + + b.ToTable("images_ft_search"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnName("id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnName("concurrency_stamp") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnName("role_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.Property("NormalizedName") + .HasColumnName("normalized_role_name") + .HasColumnType("character varying(256)") + .HasMaxLength(256); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasName("RoleNameIndex"); + + b.ToTable("roles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ClaimType") + .HasColumnName("claim_type") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnName("claim_value") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnName("role_id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("roles_claims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ClaimType") + .HasColumnName("claim_type") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnName("claim_value") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnName("user_id") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("users_claims"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnName("login_provider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnName("provider_key") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnName("provider_display_name") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnName("user_id") + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("users_logins"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnName("users_id") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnName("roles_id") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("users_roles"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnName("id") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnName("login_provider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnName("name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnName("value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("users_tokens"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.Catalog", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Catalog", "ParentCatalog") + .WithMany() + .HasForeignKey("ParentCatalogId"); + + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", "User") + .WithMany("Catalogs") + .HasForeignKey("UserId"); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageCatalog", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Catalog", "Catalog") + .WithMany("ImageCatalogs") + .HasForeignKey("CatalogId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ImageBase.WebApp.Data.Models.Image", "Image") + .WithMany("ImageCatalogs") + .HasForeignKey("ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageFtSearch", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Image", "Image") + .WithOne("ImageFtSearch") + .HasForeignKey("ImageBase.WebApp.Data.Models.ImageFtSearch", "ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ImageBase.WebApp/Migrations/20201204105242_FTSIntegration.cs b/ImageBase.WebApp/Migrations/20201204105242_FTSIntegration.cs new file mode 100644 index 0000000..800c1f5 --- /dev/null +++ b/ImageBase.WebApp/Migrations/20201204105242_FTSIntegration.cs @@ -0,0 +1,127 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; + +namespace ImageBase.WebApp.Migrations +{ + public partial class FTSIntegration : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs"); + + migrationBuilder.CreateTable( + name: "images_ft_search", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + image_id = table.Column(nullable: false), + image_vector = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_images_ft_search", x => x.id); + table.ForeignKey( + name: "FK_images_ft_search_images_image_id", + column: x => x.image_id, + principalTable: "images", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_images_ft_search_image_id", + table: "images_ft_search", + column: "image_id", + unique: true); + + migrationBuilder.AddForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs", + column: "parent_catalog_id", + principalTable: "catalogs", + principalColumn: "id", + onDelete: ReferentialAction.Restrict); + migrationBuilder.Sql(@"CREATE OR REPLACE FUNCTION make_tsvector(title TEXT, key_words TEXT,description TEXT) + RETURNS tsvector AS $$ + BEGIN + RETURN (setweight(to_tsvector('english', coalesce(title,'')),'A') || + setweight(to_tsvector('russian', coalesce(title,'')), 'A')|| + setweight(to_tsvector('english', coalesce(key_words,'')), 'B')|| + setweight(to_tsvector('russian', coalesce(key_words,'')), 'B')|| + setweight(to_tsvector('english', coalesce(description,'')), 'C')|| + setweight(to_tsvector('russian', coalesce(description,'')), 'C')); + END + $$ LANGUAGE 'plpgsql' IMMUTABLE;"); + migrationBuilder.Sql(@"CREATE OR REPLACE FUNCTION image_tsvector_trigger() RETURNS trigger AS $$ + BEGIN + INSERT INTO images_ft_search (image_id,image_vector) + VALUES (NEW.Id, make_tsvector(NEW.title,NEW.key_words,NEW.description)) + ON CONFLICT (image_id) DO UPDATE + SET image_id = NEW.Id, + image_vector = make_tsvector(NEW.title,NEW.key_words,NEW.description); + RETURN NEW; + END + $$ LANGUAGE plpgsql;"); + migrationBuilder.Sql(@"CREATE EXTENSION rum;"); + + migrationBuilder.Sql(@"CREATE INDEX IF NOT EXISTS image_vector_idx ON ""images_ft_search"" + USING RUM (""image_vector"");"); + migrationBuilder.Sql(@"CREATE TRIGGER image_search_vector_update + AFTER INSERT OR UPDATE OF title,key_words,description ON ""images"" + FOR EACH ROW EXECUTE PROCEDURE image_tsvector_trigger();"); + migrationBuilder.Sql(@"CREATE OR REPLACE FUNCTION setweight(query tsquery, weights text) RETURNS tsquery AS $$ + SELECT regexp_replace( + query::text, + '(?<=[^ !])'':?(\*?)A?B?C?D?', ''':\1'||weights, + 'g' + )::tsquery; + $$ LANGUAGE SQL IMMUTABLE;"); + migrationBuilder.Sql(@"CREATE OR REPLACE FUNCTION images_fts(search_query text, weights text,query_limit int,query_offset int) RETURNS + TABLE (""id"" bigint,title text,description text,key_words text) AS + $$ + + SELECT images.id, images.title, images.description, images.key_words FROM images, ( + SELECT image_id, image_vector, image_vector <=> setweight(plainto_tsquery(search_query), weights) AS weight + + FROM images_ft_search + + WHERE image_vector @@ setweight(plainto_tsquery(search_query), weights) + + ORDER BY weight ASC + + LIMIT query_limit OFFSET query_offset) as ids + + WHERE images.id = ids.image_id + $$ LANGUAGE SQL IMMUTABLE; "); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs"); + + migrationBuilder.DropTable( + name: "images_ft_search"); + + migrationBuilder.AddForeignKey( + name: "FK_catalogs_catalogs_parent_catalog_id", + table: "catalogs", + column: "parent_catalog_id", + principalTable: "catalogs", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + migrationBuilder.Sql(@"DROP FUNCTION make_tsvector;"); + migrationBuilder.Sql(@"DROP INDEX image_vector_idx;"); + migrationBuilder.Sql(@"DROP EXTENSION rum;"); + migrationBuilder.Sql(@"DROP TRIGGER image_search_vector_update;"); + migrationBuilder.Sql(@"DROP FUNCTION images_fts;"); + migrationBuilder.Sql(@"DROP FUNCTION setweight;"); + migrationBuilder.Sql(@"DROP FUNCTION image_tsvector_trigger;"); + } + } +} diff --git a/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs b/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs index 0e38d17..5477bb2 100644 --- a/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs +++ b/ImageBase.WebApp/Migrations/AspPostgreSQLContextModelSnapshot.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using NpgsqlTypes; namespace ImageBase.WebApp.Migrations { @@ -173,6 +174,31 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("images_catalogs"); }); + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageFtSearch", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnName("id") + .HasColumnType("bigint") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property("ImageId") + .HasColumnName("image_id") + .HasColumnType("bigint"); + + b.Property("ImageVector") + .IsRequired() + .HasColumnName("image_vector") + .HasColumnType("tsvector"); + + b.HasKey("Id"); + + b.HasIndex("ImageId") + .IsUnique(); + + b.ToTable("images_ft_search"); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { b.Property("Id") @@ -329,8 +355,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) { b.HasOne("ImageBase.WebApp.Data.Models.Catalog", "ParentCatalog") .WithMany() - .HasForeignKey("ParentCatalogId") - .OnDelete(DeleteBehavior.Cascade); + .HasForeignKey("ParentCatalogId"); b.HasOne("ImageBase.WebApp.Data.Models.Authentication.User", "User") .WithMany("Catalogs") @@ -352,6 +377,15 @@ protected override void BuildModel(ModelBuilder modelBuilder) .IsRequired(); }); + modelBuilder.Entity("ImageBase.WebApp.Data.Models.ImageFtSearch", b => + { + b.HasOne("ImageBase.WebApp.Data.Models.Image", "Image") + .WithOne("ImageFtSearch") + .HasForeignKey("ImageBase.WebApp.Data.Models.ImageFtSearch", "ImageId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) diff --git a/ImageBase.WebApp/Repositories/AspPostgreSQLContext.cs b/ImageBase.WebApp/Repositories/AspPostgreSQLContext.cs index fa1cbf5..a7389db 100644 --- a/ImageBase.WebApp/Repositories/AspPostgreSQLContext.cs +++ b/ImageBase.WebApp/Repositories/AspPostgreSQLContext.cs @@ -16,6 +16,7 @@ public class AspPostgreSQLContext : IdentityDbContext public DbSet Images { get; set; } public DbSet ImageCatalogs { get; set; } public DbSet Catalogs { get; set; } + public DbSet ImageFtSearches { get; set; } public AspPostgreSQLContext(DbContextOptions options) : base(options) { @@ -44,6 +45,7 @@ private void BuildPostgresqlConfiguration(ModelBuilder builder) new ImageConfiguration(builder.Entity()); new CatalogConfiguration(builder.Entity()); new ImageCatalogConfiguration(builder.Entity()); + new ImageFtSearchConfiguration(builder.Entity()); } }