Библиотека для организации нечёткого и фонетического поиска по строкам БД на русском языке.
Она предназначена не для полнотекстового поиска по произвольным документам, а для поиска по строковым полям:
- названия товаров;
- описания процессов;
- фамилии, имена и другие ФИО;
- артикулы, коды и смешанные текстовые поля;
- любые другие строковые значения, которые уже лежат в БД.
Основная цель библиотеки — находить нужные записи даже тогда, когда пользователь:
- допускает опечатки;
- нажимает соседние клавиши;
- пишет «как слышит»;
- не знает точного написания фамилии или слова.
Ti-Soft.SearchEngine — это не замена Elasticsearch, Lucene.NET, Azure AI Search или другим большим поисковым платформам.
Библиотека решает более узкую прикладную задачу: быстрый локальный поиск по строковым значениям, которые уже загружены из БД или другого источника данных.
Она подходит для сценариев, где нужен поиск по:
- названиям товаров;
- коротким описаниям;
- ФИО;
- артикулам;
- кодам;
- справочникам;
- смешанным строковым полям.
Библиотека особенно полезна, когда Elasticsearch, Lucene.NET или отдельный поисковый сервис избыточны, а приложению нужен простой встроенный поиск без отдельной инфраструктуры.
Подходящие сценарии:
- desktop-приложения;
- малые и средние веб-приложения;
- внутренние CRM/ERP-модули;
- админки;
- локальный и offline-поиск;
- поиск по справочникам;
- поиск по ФИО и русским фамилиям.
Библиотека не предназначена для:
- индексации больших документов;
- поиска по PDF, HTML и вложениям;
- распределённых индексов;
- кластеризации;
- аналитических запросов;
- vector search;
- сложного query language;
- замены Elasticsearch в high-load production-сценариях.
Главная идея библиотеки — дать .NET-разработчику простой NuGet-пакет для прикладного поиска там, где полноценная поисковая платформа была бы лишней.
Библиотека таргетирует:
net6.0net7.0net8.0net9.0net10.0
После публикации пакета:
dotnet add package Ti-Soft.SearchEngine --version 2.0.1using SearchEngine;
public sealed record Product : ISourceData<int>
{
public int Id { get; init; }
public required string Text { get; init; }
}using SearchEngine;
List<Product> products =
[
new() { Id = 1, Text = "Красный велосипед" },
new() { Id = 2, Text = "Согласование договора" },
new() { Id = 3, Text = "Иванов Сергей Петрович" }
];
Search<int> search = new();
var prepareResult = await search.PrepareIndexResult(products);
if (prepareResult.IsFailure)
{
Console.WriteLine($"{prepareResult.Error.Code}: {prepareResult.Error.Message}");
return;
}
var result = search.FindResult(
"согласование договра",
new SearchRequest
{
MatchMode = QueryMatchMode.SoftAllTerms,
SearchType = SearchType.NearSearch,
SearchLocation = SearchLocation.BeginWord,
AcceptableCountMisprint = 1
});
if (result.IsFailure)
{
Console.WriteLine($"{result.Error.Code}: {result.Error.Message}");
return;
}
foreach (var bucket in result.Value.Items)
{
Console.WriteLine($"Ранг/дистанция: {bucket.Key}");
foreach (var id in bucket.Value.Items)
{
Console.WriteLine($" Id: {id}");
}
}Версия 2.0.1 — patch-релиз.
Исправлено распознавание русских фамилий в латинской записи для фонетического поиска.
Добавлены regression-тесты для вариантов:
Papandopulo;Papondopulo.
Оба варианта теперь корректно находятся при поиске по фамилии Папандопуло.
Версия 2.0.0 — major-релиз.
Основные изменения:
- основной production API переведён на Result-based контракт;
- для индексации используется
PrepareIndexResult(...); - для поиска используется
FindResult(...); - ожидаемые прикладные ошибки возвращаются как значение, а не через исключения;
- фонетический поиск по умолчанию переведён на BMPM-подобный алгоритм;
- MetaPhone убран из production-пути;
- добавлена поддержка русских фамилий в латинской транслитерации.
Новые интеграции следует строить на PrepareIndexResult(...) и FindResult(...).
Для подготовки индекса и выполнения поиска используйте:
PrepareIndexResult(...)FindResult(...)
Оба метода возвращают типизированный результат выполнения и не выбрасывают исключения в ожидаемых прикладных сценариях.
PrepareIndexResult(...) подготавливает поисковый индекс и возвращает ошибку,
если входные данные некорректны или индекс не удалось построить.
FindResult(...) выполняет поиск по подготовленному индексу и возвращает либо результат поиска,
либо описание ошибки выполнения.
Для API библиотеки используются:
SearchErrorSearchErrorCode
Это позволяет различать:
- ошибки входных данных;
- ошибки индексации;
- ошибки выполнения поиска;
- некорректные параметры запроса.
Строгий режим.
Все слова запроса должны найтись в записи. Это поведение удобно для большинства бизнес-сценариев, когда пользователь вводит несколько ключевых слов и ожидает именно пересечение по ним.
Мягкое объединение по словам.
Достаточно совпадения по любому слову запроса. Подходит для широкого поиска и автодополнения.
Гибридный режим.
Сначала выше ранжируются записи, где нашлось больше слов запроса. Внутри одной группы результаты дополнительно ранжируются по суммарной дистанции. Если полных совпадений нет, возвращаются частичные.
Точный поиск без допуска опечаток.
Неточный поиск с учётом опечаток и близких клавиш.
Поиск только с начала слова.
Поиск внутри слова, включая начало.
Для поиска фамилий и похожих имён можно использовать фонетический режим:
using SearchEngine;
public sealed record Person : ISourceData<int>
{
public int Id { get; init; }
public required string Text { get; init; }
}
Search<int> search = new(isPhoneticSearch: true);
var prepareResult = await search.PrepareIndexResult(
[
new Person { Id = 1, Text = "Иванов" },
new Person { Id = 2, Text = "Папандопуло" },
new Person { Id = 3, Text = "Терентьев" }
]);
if (prepareResult.IsFailure)
{
Console.WriteLine($"{prepareResult.Error.Code}: {prepareResult.Error.Message}");
return;
}
var result = search.FindResult("Папондопуло");Фонетический поиск по умолчанию использует внутренний BMPM-подобный кодировщик для русских имён и фамилий. Он рассчитан на сценарии, когда пользователь пишет фамилию «на слух» или вводит русскую фамилию в латинской транслитерации.
Примеры поддерживаемого поведения:
Папондопуло → находит Папандопуло
Забабонова → находит Забабанова
Ivanov → находит Иванов
Shcherbakov → сопоставляется с русской кириллической записью
Фонетический режим ориентирован на русские фамилии в кириллице и распространённой латинской транслитерации. Произвольные английские имена и фамилии, например Smith или Johnson, не кодируются как русские фонетические ключи.
Выбор фонетического алгоритма не является частью публичного API. Достаточно включить фонетический режим через:
Search<int> search = new(isPhoneticSearch: true);Библиотека особенно полезна, если поиск нужен по строкам БД, а не по документам целиком, и если важно:
- быстро поднять локальный поиск внутри приложения или сервиса;
- учитывать опечатки пользователя;
- учитывать фонетические ошибки при вводе фамилий;
- не падать исключениями в ожидаемых рабочих сценариях.
Библиотека использует Result-based контракт:
PrepareIndexResult(...)FindResult(...)
Новые интеграции следует строить только на этих методах.
По умолчанию библиотека использует последовательное построение индекса. Это снижает давление на память и уменьшает вероятность пауз GC.
Для серверных и batch-сценариев параллельную индексацию можно включить явно:
await search.PrepareIndexResult(
source,
forceParallel: true,
parallelProcessingThreshold: 0);dotnet build
dotnet test
dotnet pack src/SearchEngine/SearchEngine.csproj -c ReleaseПроект распространяется по лицензии MIT.
Файл LICENSE включается в репозиторий и в NuGet-пакет.