У меня есть ситуация, когда мне нужен сверхбыстрый полнотекстовый поиск с подстановочными знаками.
Ранее я использовал ТОЛЬКО хранимую процедуру SQL, которая выполняла бы поиск в таблицах с несколькими объединениями и использовала запрос с LIKE '%searchTerm%'
однако это было очень медленно для нескольких миллионов записей.
Я пробовал полнотекстовое индексирование и поиск в SQL, однако, это, кажется, не работает, так как это разбивает на слова, но мне нужно искатьсередина строк.
Переход на новую хранимую процедуру SQL, которая объединяет все поля поиска в одну строку и возвращает ее с другим столбцом идентификатора объекта, а затем кэширует весь этот объект как * 1008.* в C # (в частности, как статический объект в AppPool
) и использование логики, проверяющей IndexOf()
строки поиска, похоже, существенно повысило производительность (с примерно 10 секунд до примерно 100 миллисекунд) .
Меня беспокоит, является ли это плохим подходом или, может быть, есть еще лучшийподход?
Новая хранимая процедура SQL, которая создает строку поиска для связи с идентификатором объекта, выглядит следующим образом:
CREATE PROCEDURE [dbo].[Search_GetLookupTable]
AS
BEGIN
SELECT
ObjectId,
(Name + ' ' + OtherName + ' ' + ep.SomethingElse + ISNULL(
(
SELECT
' ' + twl.SomeBindingName
FROM
TableWithLotsOfBindings twl
WHERE
twl.ObjectId = e.ObjectId
FOR XML PATH('')
)
, '')) AS SearchString,
ep.LastActionDateTime AS OrderDate
FROM
ObjectTable e
INNER JOIN ObjectMetaData ep ON ep.ObjectId = e.ObjectId
END
GO
Затем она загружается в List<>
с моделью, котораяимеет ObjectId
и SearchString
.Затем я сохраняю это в свойстве static
в поиске class
, а затем отслеживаю последнюю загрузку DateTime
и перезагружаю его каждые 10 минут или около того.
Первоначально я также сохранял это враспределенный кэш в памяти, однако, производительность была очень плохой сериализации и передачи данных.
private static readonly List<GlobalSearchLookupModel> _CachedSearchLookupModel = new List<GlobalSearchLookupModel>();
private static DateTime _CacheSearchLookupModelDateTime = DateTime.MinValue;
List<GlobalSearchLookupModel> lookupModels = _CachedSearchLookupModel.Value;
if (lookupModels == null)
{
lookupModels = SqlClass.SearchLookupTable();
_CachedSearchLookupModel.Value.Clear();
_CachedSearchLookupModel.Value.AddRange(lookupModels);
_CachedSearchLookupModelDateTime = DateTime.UtcNow;
}
if (_CacheSearchLookupModelDateTime.AddMinutes(10) <= DateTime.UtcNow)
{
_CachedSearchLookupModelDateTime = DateTime.UtcNow;
BackgroundTaskHelper.StartBackgroundTask(
"Pre-Load Global Search Lookup Table",
() =>
{
DateTime asyncPreloadStartDateTime = DateTime.UtcNow;
_CachedSearchLookupModel.Value.Clear();
_CachedSearchLookupModel.Value.AddRange(SqlClass.SearchLookupTable());
_CachedSearchLookupModelDateTime = DateTime.UtcNow;
});
}
List<Int64> ids = lookupModels
.Where(l => l.SearchString.IndexOf(searchTerms, StringComparison.InvariantCultureIgnoreCase) >= 0)
.OrderByDescending(l => l.OrderDate)
.Select(l => l.ObjectId)
.Distinct()
.Take(maxReturn)
.ToList();