Skip to content

Commit 8d2c6c6

Browse files
committed
Refactor AssignHomographNumber to use single DB query for SecondaryOrder
Replace the two-stage approach (DB query for headword match, then in-memory filter for SecondaryOrder) with a single DB query using correlated subqueries on MorphTypes, matching the pattern in Sorting.cs. Restore the original if(HomographNumber == 0) guard — explicit values get no special handling at all; only auto-assign for new FW-Lite entries. https://claude.ai/code/session_01FJj2v135u6KdgVxoK4tRp2
1 parent fec6967 commit 8d2c6c6

2 files changed

Lines changed: 24 additions & 41 deletions

File tree

backend/FwLite/LcmCrdt/CrdtMiniLcmApi.cs

Lines changed: 24 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,10 @@ public async Task<Entry> CreateEntry(Entry entry, CreateEntryOptions? options =
517517
{
518518
options ??= CreateEntryOptions.Everything;
519519
await using var repo = await repoFactory.CreateRepoAsync();
520-
await AssignHomographNumber(entry, repo);
520+
if (entry.HomographNumber == 0)
521+
{
522+
await AssignHomographNumber(entry, repo);
523+
}
521524
await AddChanges([
522525
new CreateEntryChange(entry),
523526
..await entry.Senses.ToAsyncEnumerable()
@@ -623,10 +626,9 @@ private async ValueTask<bool> IsEntryDeleted(Guid id)
623626
}
624627

625628
/// <summary>
626-
/// Ensures homograph numbers are consistent when creating an entry.
627-
/// Always queries entries in the same "homograph scope" (same headword + SecondaryOrder)
628-
/// so that a lone existing entry at 0 gets promoted to 1 when a second entry arrives.
629-
/// Explicit (non-zero) HomographNumbers are trusted as-is (FLEx handles correctness).
629+
/// When creating a new entry whose HomographNumber is 0, find entries in the same
630+
/// "homograph scope" (same headword + SecondaryOrder) and assign the next number.
631+
/// If a lone existing entry has HomographNumber 0, promote it to 1.
630632
/// </summary>
631633
private async Task AssignHomographNumber(Entry entry, MiniLcmRepository repo)
632634
{
@@ -637,26 +639,23 @@ private async Task AssignHomographNumber(Entry entry, MiniLcmRepository repo)
637639
var newHeadword = entry.Headword(wsId);
638640
if (string.IsNullOrEmpty(newHeadword)) return;
639641

640-
// Get the SecondaryOrder for the new entry's MorphType (fall back to Stem's SecondaryOrder)
641-
var morphTypeLookup = await repo.MorphTypes.ToDictionaryAsyncEF(m => m.Kind);
642-
var stemSecondaryOrder = morphTypeLookup.TryGetValue(MorphTypeKind.Stem, out var stemMt)
643-
? stemMt.SecondaryOrder
644-
: 0;
645-
var newSecondaryOrder = morphTypeLookup.TryGetValue(entry.MorphType, out var mt)
646-
? mt.SecondaryOrder
647-
: stemSecondaryOrder;
648-
649-
// Query entries with the same headword in the default vernacular WS, then filter by SecondaryOrder
650-
var matchingEntries = (await repo.Entries
651-
.Where(e => e.Id != entry.Id && e.Headword(wsId) == newHeadword)
652-
.Select(e => new { e.Id, e.HomographNumber, e.MorphType })
653-
.ToListAsyncLinqToDB())
654-
.Where(e =>
655-
{
656-
var so = morphTypeLookup.TryGetValue(e.MorphType, out var eMt) ? eMt.SecondaryOrder : stemSecondaryOrder;
657-
return so == newSecondaryOrder;
658-
})
659-
.ToList();
642+
// Single DB query: join MorphTypes for SecondaryOrder filtering (same pattern as Sorting.cs)
643+
var morphTypes = repo.MorphTypes.ToLinqToDB();
644+
var stemOrder = morphTypes.Where(m => m.Kind == MorphTypeKind.Stem).Select(m => m.SecondaryOrder);
645+
var newSecondaryOrder = morphTypes
646+
.Where(m => m.Kind == entry.MorphType)
647+
.Select(m => (int?)m.SecondaryOrder).FirstOrDefault()
648+
?? stemOrder.FirstOrDefault();
649+
650+
var matchingEntries = await (
651+
from e in repo.Entries
652+
where e.Id != entry.Id && e.Headword(wsId) == newHeadword
653+
let so = morphTypes.Where(m => m.Kind == e.MorphType)
654+
.Select(m => (int?)m.SecondaryOrder).FirstOrDefault()
655+
?? stemOrder.FirstOrDefault()
656+
where so == newSecondaryOrder
657+
select new { e.Id, e.HomographNumber }
658+
).ToListAsyncLinqToDB();
660659

661660
if (matchingEntries.Count == 0) return;
662661

@@ -671,9 +670,6 @@ private async Task AssignHomographNumber(Entry entry, MiniLcmRepository repo)
671670
maxHomograph = 1;
672671
}
673672

674-
// Explicit (non-zero) HomographNumber — trust it as-is
675-
if (entry.HomographNumber != 0) return;
676-
677673
entry.HomographNumber = maxHomograph + 1;
678674
}
679675

backend/FwLite/MiniLcm.Tests/CreateEntryTestsBase.cs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,6 @@ public async Task CreateEntry_RespectsExplicitHomographNumber()
152152
entry1.HomographNumber.Should().Be(5, "explicit HomographNumber should be preserved");
153153
}
154154

155-
[Fact]
156-
public async Task CreateEntry_ExplicitHomographNumber_PromotesLoneEntry()
157-
{
158-
var entry1 = await Api.CreateEntry(new() { LexemeForm = { { "en", "promotiontest" } } });
159-
entry1.HomographNumber.Should().Be(0, "single entry should have HomographNumber 0");
160-
161-
var entry2 = await Api.CreateEntry(new() { LexemeForm = { { "en", "promotiontest" } }, HomographNumber = 5 });
162-
entry2.HomographNumber.Should().Be(5, "explicit HomographNumber should be preserved");
163-
164-
entry1 = await Api.GetEntry(entry1.Id) ?? throw new NullReferenceException();
165-
entry1.HomographNumber.Should().Be(1, "existing lone entry should be promoted from 0 to 1");
166-
}
167-
168155
[Fact]
169156
public async Task CreateEntry_DifferentSecondaryOrder_DoesNotShareHomographNumbers()
170157
{

0 commit comments

Comments
 (0)