using Marathon.Infrastructure.Persistence.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; namespace Marathon.Infrastructure.Persistence.Configurations; internal sealed class SnapshotConfiguration : IEntityTypeConfiguration { public void Configure(EntityTypeBuilder builder) { builder.ToTable("Snapshots"); builder.HasKey(s => s.Id); builder.Property(s => s.Id).HasColumnType("INTEGER").ValueGeneratedOnAdd(); builder.Property(s => s.EventCode).HasColumnType("TEXT").IsRequired(); builder.Property(s => s.CapturedAt).HasColumnType("TEXT").IsRequired(); builder.Property(s => s.Source).HasColumnType("INTEGER").IsRequired(); builder.HasIndex(s => s.EventCode).HasDatabaseName("IX_Snapshots_EventCode"); // Snapshots is the largest table (live cadence 5–10s, 90-day retention) and // every hot read filters EventCode + CapturedAt range, often with an ORDER BY // CapturedAt. These composite indexes let SQLite satisfy the filter and the // ordering from the index instead of scanning + sorting the table. builder.HasIndex(s => new { s.EventCode, s.CapturedAt }) .HasDatabaseName("IX_Snapshots_EventCode_CapturedAt"); // Covers GetLatestPreMatchAsync: EventCode + Source filter, ORDER BY CapturedAt DESC. builder.HasIndex(s => new { s.EventCode, s.Source, s.CapturedAt }) .HasDatabaseName("IX_Snapshots_EventCode_Source_CapturedAt"); builder.HasMany(s => s.Bets) .WithOne(b => b.Snapshot) .HasForeignKey(b => b.SnapshotId) .OnDelete(DeleteBehavior.Cascade); } }