Staging to Production #51
@@ -36,7 +36,7 @@ public sealed class CvMatcherDbContext : DbContext
|
||||
entity.Property(x => x.JobDocumentId).HasMaxLength(64).IsRequired();
|
||||
entity.Property(x => x.ResultJson).IsRequired();
|
||||
entity.Property(x => x.CreatedAt).HasDefaultValueSql("SYSUTCDATETIME()");
|
||||
entity.HasIndex(x => new { x.CvDocumentId, x.JobDocumentId }).IsUnique();
|
||||
entity.HasIndex(x => new { x.CvDocumentId, x.JobDocumentId, x.Language }).IsUnique();
|
||||
});
|
||||
|
||||
modelBuilder.Entity<CvMatcherChatCacheEntity>(entity =>
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CvMatcher.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
namespace CvMatcher.Data.Migrations
|
||||
{
|
||||
[DbContext(typeof(CvMatcherDbContext))]
|
||||
[Migration("20260529111000_UpdateResultsUniqueConstraintToIncludeLanguage")]
|
||||
[Migration("20260601131043_UpdateResultsUniqueConstraintToIncludeLanguage")]
|
||||
partial class UpdateResultsUniqueConstraintToIncludeLanguage
|
||||
{
|
||||
/// <inheritdoc />
|
||||
@@ -80,7 +80,7 @@ namespace CvMatcher.Data.Migrations
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ResultJson")
|
||||
.IsRequired()
|
||||
+23
-10
@@ -1,5 +1,4 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using CvMatcher.Data;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
@@ -11,18 +10,23 @@ namespace CvMatcher.Data.Migrations
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
// The Language column was added in migration 20260524140335, but the unique constraint
|
||||
// was never updated from (CvDocumentId, JobDocumentId) to include Language.
|
||||
// This caused duplicate key violations when matching the same CV+Job in different languages.
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Results_CvDocumentId_JobDocumentId",
|
||||
schema: MigrationConstants.SchemaName,
|
||||
schema: "cvMatcher",
|
||||
table: "Results");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Language",
|
||||
schema: "cvMatcher",
|
||||
table: "Results",
|
||||
type: "nvarchar(450)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(max)");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Results_CvDocumentId_JobDocumentId_Language",
|
||||
schema: MigrationConstants.SchemaName,
|
||||
schema: "cvMatcher",
|
||||
table: "Results",
|
||||
columns: new[] { "CvDocumentId", "JobDocumentId", "Language" },
|
||||
unique: true);
|
||||
@@ -33,12 +37,21 @@ namespace CvMatcher.Data.Migrations
|
||||
{
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Results_CvDocumentId_JobDocumentId_Language",
|
||||
schema: MigrationConstants.SchemaName,
|
||||
schema: "cvMatcher",
|
||||
table: "Results");
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "Language",
|
||||
schema: "cvMatcher",
|
||||
table: "Results",
|
||||
type: "nvarchar(max)",
|
||||
nullable: false,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "nvarchar(450)");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Results_CvDocumentId_JobDocumentId",
|
||||
schema: MigrationConstants.SchemaName,
|
||||
schema: "cvMatcher",
|
||||
table: "Results",
|
||||
columns: new[] { "CvDocumentId", "JobDocumentId" },
|
||||
unique: true);
|
||||
@@ -1,4 +1,4 @@
|
||||
// <auto-generated />
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using CvMatcher.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@@ -77,7 +77,7 @@ namespace CvMatcher.Data.Migrations
|
||||
|
||||
b.Property<string>("Language")
|
||||
.IsRequired()
|
||||
.HasColumnType("nvarchar(max)");
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<string>("ResultJson")
|
||||
.IsRequired()
|
||||
@@ -88,7 +88,7 @@ namespace CvMatcher.Data.Migrations
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("CvDocumentId", "JobDocumentId")
|
||||
b.HasIndex("CvDocumentId", "JobDocumentId", "Language")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("Results", "cvMatcher");
|
||||
|
||||
@@ -48,18 +48,27 @@ public sealed class EfMatcherRepository : IMatcherRepository
|
||||
|
||||
if (exists) return;
|
||||
|
||||
_db.CvMatchResults.Add(new CvMatchResultEntity
|
||||
try
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
CvDocumentId = cvDocumentId,
|
||||
JobDocumentId = jobDocumentId,
|
||||
Language = language,
|
||||
ResultJson = JsonSerializer.Serialize(response, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
|
||||
Score = response.Score,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
_db.CvMatchResults.Add(new CvMatchResultEntity
|
||||
{
|
||||
Id = Guid.NewGuid().ToString("N"),
|
||||
CvDocumentId = cvDocumentId,
|
||||
JobDocumentId = jobDocumentId,
|
||||
Language = language,
|
||||
ResultJson = JsonSerializer.Serialize(response, new JsonSerializerOptions(JsonSerializerDefaults.Web)),
|
||||
Score = response.Score,
|
||||
CreatedAt = DateTime.UtcNow
|
||||
});
|
||||
|
||||
await _db.SaveChangesAsync(ct);
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
catch (DbUpdateException ex) when (ex.InnerException?.Message.Contains("IX_Results_CvDocumentId_JobDocumentId_Language") == true
|
||||
|| ex.InnerException?.Message.Contains("unique") == true)
|
||||
{
|
||||
// Duplicate key violation: record was inserted between the AnyAsync check and SaveChangesAsync.
|
||||
// This is safe to ignore — the match result already exists in the database.
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<string?> GetChatCompletionAsync(string cacheKey, CancellationToken ct)
|
||||
|
||||
Reference in New Issue
Block a user