Skip to content

Commit 2c23a49

Browse files
committed
Single Finalize method; add commented-out morph token SQL expressions
- Merge Entry Finalize overloads into one that always requires morphTypeDataLookup — an entry can't be finalized without it - Add commented-out HeadwordSearchValue expression and MorphTypeData JOIN patterns showing exactly how morph tokens will work in SQL once MorphTypeData becomes a CRDT entity - Update TODOs in Filtering.cs and EntrySearchService.cs to reference the new HeadwordSearchValue pattern (all WSs, not per-wsId) https://claude.ai/code/session_01GFNCNDE5wHE2hGC7pQQp2f
1 parent d075473 commit 2c23a49

4 files changed

Lines changed: 49 additions & 13 deletions

File tree

backend/FwLite/LcmCrdt/Data/EntryQueryHelpers.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,47 @@ public static string HeadwordWithTokens(this Entry e, WritingSystemId ws, string
3737
: ((leading ?? "") + Json.Value(e.LexemeForm, ms => ms[ws]) + (trailing ?? "")).Trim()
3838
: Json.Value(e.CitationForm, ms => ms[ws])!.Trim();
3939

40+
// === Morph-token-aware headword expressions (ready for when MorphTypeData is a CRDT entity) ===
41+
//
42+
// Once MorphTypeData is in the DB, use these in place of the token-free versions above.
43+
//
44+
// Usage in queries (e.g. EntrySearchService.FilterInternal, Filtering.cs):
45+
//
46+
// var morphTypes = dbContext.GetTable<MorphTypeData>();
47+
//
48+
// from entry in entries
49+
// from morphData in morphTypes.LeftJoin(m => (int)m.MorphType == (int)entry.MorphType)
50+
// let headword = entry.HeadwordWithTokens(wsId, morphData.LeadingToken, morphData.TrailingToken)
51+
// ...
52+
//
53+
// For searching headwords (with tokens) across ALL writing systems:
54+
//
55+
// [ExpressionMethod(nameof(HeadwordSearchValueExpression))]
56+
// public static bool HeadwordSearchValue(Entry e, string? leading, string? trailing, string query)
57+
// {
58+
// return e.CitationForm.SearchValue(query)
59+
// || e.LexemeForm.Values.Any(kvp =>
60+
// SqlHelpers.ContainsIgnoreCaseAccents((leading ?? "") + kvp.Value + (trailing ?? ""), query));
61+
// }
62+
//
63+
// private static Expression<Func<Entry, string?, string?, string, bool>> HeadwordSearchValueExpression() =>
64+
// (e, leading, trailing, query) =>
65+
// Json.QueryValues(e.CitationForm).Any(v => SqlHelpers.ContainsIgnoreCaseAccents(v, query))
66+
// || Json.QueryValues(e.LexemeForm).Any(v =>
67+
// SqlHelpers.ContainsIgnoreCaseAccents((leading ?? "") + v + (trailing ?? ""), query));
68+
//
69+
// Then in queries:
70+
//
71+
// from morphData in morphTypes.LeftJoin(m => (int)m.MorphType == (int)entry.MorphType)
72+
// where entry.HeadwordSearchValue(morphData.LeadingToken, morphData.TrailingToken, query)
73+
// || entry.Senses.Any(s => s.Gloss.SearchValue(query))
74+
//
75+
// And for the FTS table (ToEntrySearchRecord), join MorphTypeData to get tokens:
76+
//
77+
// from entry in entries
78+
// from morphData in morphTypes.LeftJoin(m => (int)m.MorphType == (int)entry.MorphType)
79+
// let headword = entry.HeadwordWithTokens(ws.WsId, morphData.LeadingToken, morphData.TrailingToken)
80+
4081
/// <summary>
4182
/// Computes headwords for all writing systems present in CitationForm or LexemeForm,
4283
/// applying morph tokens when CitationForm is absent.

backend/FwLite/LcmCrdt/Data/Filtering.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ public static IQueryable<Entry> WhereExemplar(
1515
return query.Where(e => e.Headword(ws).StartsWith(exemplar));
1616
}
1717

18-
// TODO: When morph tokens are added, this should also check headword with tokens.
19-
// See EntrySearchService.FilterInternal for the pattern (needs wsId parameter).
18+
// TODO: When morph tokens are added, also check headword with tokens across all WSs.
19+
// Use the pattern from EntryQueryHelpers.HeadwordSearchValue (currently commented out).
2020
public static Expression<Func<Entry, bool>> SearchFilter(string query)
2121
{
2222
return e => e.LexemeForm.SearchValue(query)

backend/FwLite/LcmCrdt/FullTextSearch/EntrySearchService.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,9 @@ where Sql.Ext.SQLite().Match(searchRecord, ftsString) &&
7272
(entry.LexemeForm.SearchValue(query)
7373
|| entry.CitationForm.SearchValue(query)
7474
|| entry.Senses.Any(s => s.Gloss.SearchValue(query))
75-
// Headword includes morph tokens (e.g. "-" prefix for suffixes) that aren't in
76-
// LexemeForm/CitationForm. Without this, FTS matches on token-decorated headwords
77-
// would be excluded by the post-filter. Currently only checks the active WS;
78-
// when MorphTypeData becomes a CRDT entity, Headword() will include real tokens.
75+
// TODO: When MorphTypeData is a CRDT entity, replace the line below with
76+
// HeadwordSearchValue to match morph-token-decorated headwords across ALL WSs.
77+
// See EntryQueryHelpers for the commented-out HeadwordSearchValue expression.
7978
|| SqlHelpers.ContainsIgnoreCaseAccents(entry.Headword(wsId), query))
8079
let headword = entry.Headword(wsId)
8180
let headwordMatches = SqlHelpers.ContainsIgnoreCaseAccents(headword, query)
@@ -266,7 +265,8 @@ private static EntrySearchRecord ToEntrySearchRecord(Entry entry, WritingSystem[
266265
{
267266
// Include headwords for ALL vernacular writing systems (space-separated), matching how
268267
// LexemeForm and CitationForm already work. This ensures FTS matches across all WS.
269-
// TODO: Include morph tokens once MorphTypeData is available as a CRDT entity.
268+
// TODO: Include morph tokens once MorphTypeData is a CRDT entity.
269+
// See EntryQueryHelpers for the commented-out JOIN pattern.
270270
var headword = string.Join(" ",
271271
writingSystems.Where(ws => ws.Type == WritingSystemType.Vernacular)
272272
.Select(ws => entry.Headword(ws.WsId))

backend/FwLite/LcmCrdt/QueryHelpers.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@ public static class QueryHelpers
88
public static void Finalize(this Entry entry,
99
IComparer<ComplexFormComponent> complexFormComparer,
1010
IReadOnlyDictionary<MorphType, MorphTypeData> morphTypeDataLookup)
11-
{
12-
entry.Finalize(complexFormComparer);
13-
entry.Headword = EntryQueryHelpers.ComputeHeadwords(entry, morphTypeDataLookup);
14-
}
15-
16-
public static void Finalize(this Entry entry, IComparer<ComplexFormComponent> complexFormComparer)
1711
{
1812
entry.Senses.ApplySortOrder();
1913
entry.Components.ApplySortOrder();
@@ -22,6 +16,7 @@ public static void Finalize(this Entry entry, IComparer<ComplexFormComponent> co
2216
{
2317
sense.Finalize();
2418
}
19+
entry.Headword = EntryQueryHelpers.ComputeHeadwords(entry, morphTypeDataLookup);
2520
}
2621

2722
public static void Finalize(this Sense sense)

0 commit comments

Comments
 (0)