Быстрый полнотекстовый поиск с использованием String.IndexOf или MS SQL - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть ситуация, когда мне нужен сверхбыстрый полнотекстовый поиск с подстановочными знаками.

Ранее я использовал ТОЛЬКО хранимую процедуру 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();

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Вы можете попытаться создать свой собственный индекс поиска в SQL, используя следующую идею:

Допустим, ваша таблица

MyTable(Id bigint Primary Key, Text nvarchar(max))

, где Textстолбец, в котором вы хотите искать.

Затем создайте таблицу

IndexTable(TextIndex nvarchar(max) Primary Key, Id bigint)

, с Id внешним ключом для MyTable.

Теперь вы заполняете этотаблица с всеми суффиксами текстов, содержащихся в MyTable.

Теперь вы можете переписать ваш запрос

SELECT * 
FROM MyTable 
WHERE Text LIKE '%searchTerm%'

to

SELECT * 
FROM MyTable 
WHERE Id IN (SELECT Id FROM IndexTable WHERE TextIndex LIKE 'searchTerm%')

(Этот запрос также можно написать с помощью объединения, нозатем может привести к дублированию)

Это должен быть эффективный запрос, так как LIKE 'searchTerm%' может использовать PK-индекс IndexTable.

И, наконец, вы можете сохранить эту таблицу додата с триггерами.

0 голосов
/ 12 февраля 2019

Нет такого решения, доступного в любой современной базе данных SQL, о которой я знаю.Они просто не предназначены для таких случаев использования, как ваш.

Вам действительно нужно взглянуть на такие решения, как Упругий поиск или Поиск Azure для такого решения, даже те, которые требуют использования специальных конструкций и фильтров для достиженияФункция поиска по шаблону.

...