Я работаю над приложением, работающим в Windows Mobile 6, которое должно иметь возможность извлекать все элементы из таблицы элементов, содержащей заданную строку (предоставленную конечным пользователем) в поле описания элемента. Проблема в том, что в таблице примерно 170 000 предметов. Так как мне нужно вернуть все элементы, которые содержат строку в любом месте описания, я вынужден использовать LIKE% string%, что исключает любую возможность использования индекса. Структура данных и таблиц изначально основана на базе данных Progress, в которой есть замечательный оператор содержит любые поля, индексированные по словам. Это не относится к нашему мобильному приложению, поскольку оно использует SQL Server Compact 3.5.
По сути, мой DAL выполняет запрос и получает SqlCeDataReader, а затем использует ItemFactory для создания объекта List, который содержит только совпадающие элементы. Это, очевидно, позволяет нам отделять наши доменные / бизнес-объекты от уровня доступа к данным.
Прекрасно и прекрасно, за исключением 8 м и 42-х, которые требуются для извлечения предметов, когда я ищу все предметы, которые содержат в описании что-то вроде «гольф». Очевидно, что это не приемлемый период времени для конечного пользователя.
Моя первая попытка состояла в том, чтобы вместо этого извлечь все элементы обратно из базы данных, используя SELECT * FROM Item "(с предложением order by в одном из основных проиндексированных полей). В этот момент я запустил проверку IndexOf, проходя через SqlCeDataReader и ItemFactory добавляли элементы в объект List только в том случае, если они содержали запрошенный текст описания. Это улучшило скорость до 1 м 46 с. Не слишком потертый, но все же слишком медленный.
Затем я попробовал другой подход, который показал обещание ... почти ... Во время запуска приложения я попытался создать список, который содержал бы все объекты элементов в базе данных (для выполнения запроса и заполнения всего целого требуется около 2 минут). список, но по крайней мере это только один раз, поскольку приложение инициализируется ... все еще ... тьфу). Как только список заполнен, я могу легко выполнить запросы к этому списку, выполнив следующие действия (надеюсь, мой синтаксис правильный ... Сейчас я не на работе и у меня нет Visual Studio на компьютере. сижу))
List<Item> specificItems =
AllItems.FindAll(i => i.Description.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0);
Этот подход сбил его до 21 с. Очень хорошо (все еще медленно, хотя в великой схеме вещей). Однако проблема в том, что использование памяти слишком велико, если я загружаю все элементы из базы данных. Мне пришлось фактически отрезать последние 20 000 элементов (поэтому временные рамки 21-го года, вероятно, были бы больше похожи на 25-е) во время начальной загрузки, потому что генерировалось исключение OutOfMemoryException. Согласно диспетчеру памяти на эмуляторе, у меня все еще было около 20 МБ свободной оперативной памяти, но я слышал, что процессу может быть назначено только 32 МБ или ОЗУ (не уверен, правда ли это для WM 6, но, похоже, поэтому).
Чтобы убедиться, что это было не потому, что я использовал объект List для хранения всех элементов (которые я инстанцировал с необходимой емкостью в его конструкторе, чтобы избежать динамического изменения размера), который я также прочитал, может вызвать дополнительную память Использование, когда это подразумевает вызов EnsureCapacity, я попытался использовать массив Item [] (размер заранее). Это все еще имело проблему с памятью, и разница в размерах была незначительной.
Хорошо, довольно бессвязно. Я знаю, что мне, вероятно, придется до некоторой степени ограничивать записи, возвращаемые из базы данных устройством чтения данных (через некоторый индексированный поиск по полю другого типа), и затем, вероятно, будет использовать indexOf для этого меньшего поднабора элементов, чтобы получить максимальную производительность. (таким образом пропуская оператор Like все вместе). Это приведет к тому, что конечному пользователю придется вводить нечто большее, чем просто поиск по описанию (возможно, информацию об иерархии элементов, чтобы ограничить тип элементов для поиска в них).
Есть идеи? Я поступаю неправильно?
Спасибо, что выслушали (извините, этот пост длинный, я немного обдумал).
О, я должен добавить (просто вкратце), что я использую:
- Windows Mobile 6
- Sql Server Compact Edition 3.5
- C # 3,5
ОБНОВЛЕНИЕ: хотя упомянутый ниже подход «Фильтр Блума» показался мне интересным, я не смог выполнить одно требование (которое я не указывал выше). Я не могу сопоставить слова, содержащиеся в других словах (например, «клуб» не возвращает «клубы»). Из-за этого я был вынужден использовать другой подход (Кент Фредрик ... спасибо за указание на это). Я пометил ответ Кента как правильный, так как его подход отвечал большинству требований (Митч, у вас была та же проблема, что и у фильтра Блума, предложенного Jaunder). Тем не менее, я пошел другим путем (на данный момент ...), чем его путь.
То, что я сделал, это перетянул все объекты предметов в память, только с номерами предметов и описаниями (что ограничивает память, но все равно вызывает более длительную инициализацию, чем мне нравится ... многопоточность и загрузка этой информации за кадром, пока приложение работает, я могу позаботиться об этом, я думаю). Для выполнения поиска я написал свою собственную подпрограмму. Подпрограмма написана в неуправляемом коде c #, который использует два указателя и пару циклов для запуска описания и необходимого соответствующего текста. Если он находит совпадение где-либо в описании, он добавляет номер элемента в массив. После поиска всех элементов новый запрос возвращается в базу данных и получает только совпадающие номера элементов (что очень быстро благодаря индексу в целочисленном поле). Затем эти элементы создаются в Списке со всей информацией (не только номером элемента и описанием). Вся операция занимает приблизительно 5 - 10 секунд (в зависимости от описания), что на данный момент достаточно.
Я по-прежнему буду углубляться в оптимизацию этого (возможно, удастся отследить, сколько символов соответствует поисковому запросу ... если в описании элемента осталось меньше символов, чем в требуемом тексте, цикл может продолжиться прямо до следующий товар).
Любые предложения по-прежнему приветствуются. Пока я отметил ответ Кента как «самый правильный» на мой вопрос.
Рекомендую Дольчу помочь мне написать рутину содержания.