Skip to content

LT-22524: Add substring search mode to StringSearcher - #395

Open
thejambi wants to merge 1 commit into
masterfrom
LT-22524
Open

LT-22524: Add substring search mode to StringSearcher#395
thejambi wants to merge 1 commit into
masterfrom
LT-22524

Conversation

@thejambi

@thejambi thejambi commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Add a Substring value to SearchType that matches the query anywhere within a string, case- and diacritic-insensitive, backed by a raw-string index scanned with CompareInfo.IndexOf. The existing Exact/Prefix/FullText modes are unchanged. This change enables a change in FieldWorks to use this new SearchType, see sillsdev/FieldWorks#1069

https://jira.sil.org/browse/LT-22524


This change is Reviewable

@github-actions

github-actions Bot commented Aug 14, 2026

Copy link
Copy Markdown

LCM Tests

    16 files  ± 0      16 suites  ±0   1m 59s ⏱️ -7s
 2 878 tests + 3   2 858 ✅ + 3   20 💤 ±0  0 ❌ ±0 
11 460 runs  +12  11 292 ✅ +12  168 💤 ±0  0 ❌ ±0 

Results for commit 48bd1aa. ± Comparison against base commit 9fdb060.

♻️ This comment has been updated with latest results.

Add a Substring value to SearchType that matches the query anywhere
within a string, case- and diacritic-insensitive, backed by a
raw-string index scanned with CompareInfo.IndexOf. The existing
Exact/Prefix/FullText modes are unchanged.
@thejambi
thejambi marked this pull request as ready for review August 17, 2026 17:32
return Enumerable.Empty<T>();
CompareInfo ci = CultureInfo.InvariantCulture.CompareInfo;
return raw.Where(kv => ci.IndexOf(kv.Value, text,
CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0).Select(kv => kv.Key);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might need to consider if this will work correctly for us, or if we need to create a manged wrapper in icu-dotnet for string search so we can use what ICU provides for this use case.

@hahn-kev hahn-kev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. I left some suggestions, nothing blocking.

One thing, since the issue specifically calls out matching how FW Lite search works, here's some of our tests which would be relevant here
https://github.com/sillsdev/languageforge-lexbox/blob/bbcc058f267a789302b9570e9542d30968199a3f/backend/FwLite/MiniLcm.Tests/QueryEntryTestsBase.cs#L412-L455

Up to you how closely you try to match what FW Lite does.

#endregion SortKeyIndex class

private readonly Dictionary<Tuple<int, int>, SortKeyIndex> m_indices = new Dictionary<Tuple<int, int>, SortKeyIndex>();
private readonly Dictionary<Tuple<int, int>, List<KeyValuePair<T, string>>> m_rawIndices = new Dictionary<Tuple<int, int>, List<KeyValuePair<T, string>>>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wish I had a suggestion, but this type is really ugly lol. I might replace KeyValuePair with a custom struct which does the same thing but is more understandable what's going on when you look at it.

Another idea (bigger tweak) is to create a custom struct (maybe named SearchBucket or IndexKey) which is a new struct that replaces all the uses of Tuple<int, int> (which is quite meaning less IMO), you would just need to match the compare and equality rules of the Tuple. This more falls in line with leaving code better than when you found it.

/// </summary>
public void Add(T item, int indexId, int wsId, string text)
{
SortKeyIndex index = GetIndex(indexId, wsId);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the new SearchType we're now getting the index and never using it, it also is creating the index if it doesn't exist which isn't great since it's never used. The same issue exists on the Search method.

break;

case SearchType.Substring:
GetRawIndex(indexId, wsId).Add(new KeyValuePair<T, string>(item, text ?? string.Empty));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we're handling the case of a null text argument here by using an empty string, but searching for empty strings is explicitly banned. Should we just not add a value to the index if the text is null or empty? We could probably safely do this on the first line of this method. Taking a quick look at the EntryGoSearchEngine and others it looks like they never call Add with an empty string so that's probably safe to change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants